<template>
  <div ref="processModule" class="process-module" :class="{'is-publishing': isFlowPubulish, 'locked': locked}">
    <vueProcess
      v-if="viewMode === 'vueprocess'"
      ref="process"
      :shapes="shapes"
      :value="originalData"
      :ctx-menu-options="nodeMenuOptions"
      :view-options="viewOptions"
      :disabled="workflowIsExecutor || myReadonly"
      :newTipVisible="newTipVisible"
      @toggle-shape="toggleShape"
      @change="change"
      @message="message"
      @node-click="click"
      @node-dblclick="dblclick"
      @node-delete="nodeDelete"
      @add="addNode"
      @on-ctx-menu="onContextMenu"
      @search-node-path="showSearchPath"
      @link-delete="linkDelete"
      @changeViewMode="handleSwitchViewMode"
      @link-add="linkAdd">
      <DesignToolbar
        viewMode="vueprocess"
        :readonly="myReadonly"
        :workflowIsExecutor="workflowIsExecutor"
        :needReRun="needReRun"
        :isFlowPubulish="isFlowPubulish"
        :isFlowSubmit="isFlowSubmit"
        :isLatest="isLatest"
        :publish="publish"
        :product="product"
        :flowId="flowId"
        :flowStatus="flowStatus"
        :associateGit="associateGit"
        :isFlowSubmited="isFlowSubmited"
        :isMainFlow="isMainFlow"
        :type="type"
        @click-itembar="handleClickToolbar"
      />
    </vueProcess>
    <div v-else class="designer" :style="{ 'z-index': isfullScreen ? 6 : 1}">
      <div class="designer-toolbar">
        <DesignToolbar
          :viewMode="viewMode"
          :readonly="myReadonly"
          :workflowIsExecutor="workflowIsExecutor"
          :needReRun="needReRun"
          :isFlowPubulish="isFlowPubulish"
          :isFlowSubmit="isFlowSubmit"
          :isLatest="isLatest"
          :publish="publish"
          :product="product"
          :flowId="flowId"
          :flowStatus="flowStatus"
          :associateGit="associateGit"
          :isFlowSubmited="isFlowSubmited"
          :isMainFlow="isMainFlow"
          :type="type"
          @click-itembar="handleClickToolbar"
        />
      </div>
      <cyeditor v-if="viewMode === 'cyeditor'"
        style="top: 36px"
        ref="process"
        :shapes="shapes"
        :value="originalData || {nodes:[],edges: []}"
        :ctx-menu-options="nodeMenuOptions"
        :disabled="workflowIsExecutor || myReadonly"
        :readable="myReadonly"
        :viewMode="viewMode"
        :newTipVisible="newTipVisible"
        @screenSizeChange="screenSizeChange"
        @change="change"
        @message="message"
        @node-click="click"
        @node-dblclick="dblclick"
        @node-delete="nodeDelete"
        @node-add="addNode"
        @on-ctx-menu="onContextMenu"
        @link-add="linkAdd"
        @search-node-path="showSearchPath"
        @changeViewMode="handleSwitchViewMode"
      />
      <template v-if="viewMode === 'table'">
        <iframe class="iframeClass" id="iframe" ref="ifr" style="padding-top:36px" :src="tableViewUrl" frameborder="0" width="100%" height="100%" />
        <Spin v-if="iframeloading" fix>{{ $t('message.common.Loading') }}</Spin>
      </template>
    </div>
    <div
      class="process-module-param"
      v-clickoutside="handleOutsideClick"
      v-show="isParamModalShow">
      <div class="process-module-param-modal-header">
        <h5>{{ isResourceShow ? $t('message.workflow.process.resource') : isDispatch ? $t('message.workflow.process.schedule') : $t('message.workflow.process.params') }}{{$t('message.workflow.process.seting')}}</h5>
      </div>
      <div class="process-module-param-modal-content">
        <argument
          v-show="!isResourceShow"
          :props="props"
          :isDispatch="isDispatch"
          :scheduleParamsProp="scheduleParams"
          @change-schedule="onScheduleChange"
          @change-props="onPropsChange"></argument>
        <resource
          v-show="isResourceShow"
          :resources="resources"
          :flow-name="name"
          @update-resources="updateResources"></resource>
      </div>
    </div>
    <div
      class="process-module-param"
      v-show="nodebaseinfoShow"
      @click="clickBaseInfo">
      <div class="process-module-param-modal-header">
        <h5>{{$t('message.workflow.process.baseInfo')}}</h5>
        <div class="save-button">
          <Button  v-if="!myReadonly" size="small" @click.stop="saveNodeParameter" :loading="saveLoading"
            :disabled="false">{{$t('message.workflow.process.nodeParameter.BC')}}
          </Button>
        </div>
        <Icon class="close-icon" type="md-close" size="16" @click.stop="closeParamsBar"></Icon>
      </div>
      <div class="process-module-param-modal-content">
        <nodeParameter
          ref="nodeParameter"
          :node-data="clickCurrentNode"
          :name="name"
          :readonly="myReadonly"
          :nodes="json && json.nodes"
          :consoleParams="consoleParams"
          :tabs="tabs"
          @saveNode="saveNode"
          @saveButtonStatus="saveButtonStatus"
        ></nodeParameter>
      </div>
    </div>
    <Modal
      width="450"
      v-model="saveModal">
      <div
        class="process-module-title"
        slot="header">
        {{$t('message.workflow.process.save')}}
      </div>
      <Form
        ref="formSave"
        :model="saveModel"
        :label-width="85" >
        <FormItem
          :label="$t('message.workflow.comment')"
          prop="comment"
          :rules="[{ required: true, message: $t('message.workflow.process.inputComment') },{message: $t('message.workflow.process.commentLengthLimit'), max: 255}]">
          <Input
            v-model="saveModel.comment"
            type="textarea"
            :placeholder="$t('message.workflow.process.inputComment')"
            style="width: 300px;" />
        </FormItem>
      </Form>
      <div slot="footer">
        <Button
          type="primary"
          @click="handleSave">{{$t('message.workflow.process.confirmSave')}}</Button>
      </div>
    </Modal>
    <Spin
      v-if="loading"
      size="large"
      fix/>
    <Modal
      v-model="repetitionNameShow"
      :title="$t('message.workflow.process.nodeNameNotice')"
      class="repetition-name">
      {{$t('message.workflow.process.repeatNode')}}{{ repeatTitles.join(', ') }}
      <div slot="footer">
        <Button
          type="primary"
          @click="repetitionName">{{$t('message.workflow.ok')}}</Button>
      </div>
    </Modal>
    <associate-script
      ref="associateScript"
      @click="associateScript"/>
    <generate-datachecker ref="datachecker" @confirm="addDatachecker"/>
    <!-- 创建节点弹窗 -->
    <Modal
      :title="addNodeTitle"
      v-model="addNodeShow"
      :closable="false"
      :mask-closable="false"
    >
      <Form
        v-if="createNodeParamsList.length > 0"
        label-position="left"
        :label-width="130"
        ref="addFlowfoForm"
        :model="clickCurrentNode"
        :rules="formRules">
        <template v-for="item in createNodeParamsList">
          <FormItem v-if="['Input', 'Text', 'Disable'].includes(item.uiType)" :key="item.key" :label="item.lableName" :prop="item.key" >
            <Input v-model="clickCurrentNode[item.key]" :type="filterFormType(item.uiType)"
              :placeholder="item.desc" :disabled="item.uiType === 'Disable'"
            />
          </FormItem>
          <FormItem v-if="item.uiType === 'Select'" :key="item.key" :label="item.lableName" :prop="item.key" >
            <Select
              v-model="clickCurrentNode[item.key]"
              :placeholder="item.desc">
              <Option v-for="subItem in JSON.parse(item.value)" :value="subItem" :key="subItem">{{subItem}}</Option>
            </Select>
          </FormItem>
          <FormItem v-if="item.uiType === 'Binding'" :key="item.key" :label="item.lableName" :prop="item.key" >
            <Select
              v-model="clickCurrentNode[item.key]"
              :placeholder="item.desc">
              <Option v-for="subItem in conditionBindList(item)" :value="subItem.key" :key="subItem.key">{{subItem.name}}</Option>
            </Select>
          </FormItem>
        </template>
      </Form>
      <div slot="footer">
        <Button
          type="text"
          size="large"
          @click="addFlowCancel">{{$t('message.workflow.cancel')}}</Button>
        <Button
          type="primary"
          size="large"
          @click="addFlowOk">{{$t('message.workflow.ok')}}</Button>
      </div>
    </Modal>
    <!-- 发布弹窗 -->
    <FlowDiffPublish
      :visible.sync="pubulishShow"
      :projectName="$route.query.projectName" 
      :orchestratorId="orchestratorId"
      :associateGit="associateGit"
    >
      <Form
        label-position="top">
        <FormItem
          :label="$t('message.workflow.desc')">
          <Input
            :rows="4"
            type="textarea"
            v-model="pubulishFlowComment"
            :placeholder="$root.$t('message.workflow.publish.inputDesc')"></Input>
        </FormItem>
        <FormItem
          v-if="associateGit"
          label="提交记录">
          <Table border :columns="publishFlowColumns" :data="publishFlowData" :height="300"></Table>
        </FormItem>
      </Form>
      <template slot="footer">
        <Button
          type="primary"
          :loading="saveingComment"
          :disabled="saveingComment"
          @click="handleWorkflowPublish">{{$t('message.workflow.ok')}}</Button>
        <Button
          @click="showDiff">{{$t('message.workflow.showVersionDiff')}}</Button>
        <Button
          @click="pubulishShow = false">{{$t('message.workflow.cancel')}}</Button>
      </template>
    </FlowDiffPublish>
    <!-- 提交弹窗 -->
    <FlowDiffSubmit
      :visible.sync="showSubmit"
      :projectName="$route.query.projectName" 
      :orchestratorId="orchestratorId" 
    >
      <Input
        :rows="2"
        type="textarea"
        v-model="submitDesc"
        :placeholder="$root.$t('message.workflow.publish.submitDesc')"></Input>
      <template slot="footer">
        <Button
          type="primary"
          :disabled="!submitDesc"
          @click="submitGit">{{$t('message.workflow.ok')}}</Button>
        <Button
          @click="showSubmit = false">{{$t('message.workflow.cancel')}}</Button>
      </template>
    </FlowDiffSubmit>
    <!-- 导出弹窗 -->
    <Modal
      v-model="workflowExportShow"
      :title="$t('message.workflow.exportWorkflow')"
      @on-ok="workflowExportOk">
      <Form
        :label-width="100"
        label-position="left"
        ref="exportForm"
      >
        <FormItem :label="$t('message.workflow.desc')" porp="desc">
          <Input
            v-model="exportDesc"
            type="textarea"
            :placeholder="$t('message.workflow.inputWorkflowDesc')"></Input>
        </FormItem>
        <FormItem porp="changeVersion">
          <Checkbox v-model="exporTChangeVersion">{{$t('message.workflow.synchronousPublishing')}}</Checkbox>
        </FormItem>
      </Form>
    </Modal>
    <!-- 批量关联上下游节点 -->
    <Modal
      v-model="addEdgesShow"
      :title="`批量关联节点（根节点：${addEdgesForm.currentNodeName}）`"
      class="repetition-name"
      @on-visible-change="cancelEdges">
      <Form
        label-position="top"
        ref="addChildrenRef"
      >
        <FormItem label="上游一级节点" >
          <Select
            v-model="addEdgesForm.upstreamNodes"
            placeholder="请选择"
            multiple
            filterable
            @on-change="changeNodes('upstream', $event)">
            <Option v-for="item in upstreamNodeList" :value="item.key" :key="item.key">{{item.title}}</Option>
          </Select>
        </FormItem>
        <FormItem label="下游一级节点" >
          <Select
            v-model="addEdgesForm.downstreamNodes"
            placeholder="请选择"
            multiple
            filterable
            @on-change="changeNodes('downstream', $event)">
            <Option v-for="item in downstreamNodeList" :value="item.key" :key="item.key">{{item.title}}</Option>
          </Select>
        </FormItem>
      </Form>
      <div slot="footer">
        <Button
          type="text"
          size="large"
          @click="cancelEdges(false)">{{$t('message.workflow.cancel')}}</Button>
        <Button
          type="primary"
          @click="addEdges">{{$t('message.workflow.ok')}}</Button>
      </div>
    </Modal>
    <!-- 运行控制台 -->
    <console
      v-if="openningNode && viewMode !== 'table'"
      ref="currentConsole"
      :node="openningNode"
      :stop="workflowIsExecutor"
      class="process-console"
      :height="consoleHeight"
      :style="getConsoleStyle"
      @close-console="closeConsole"></console>
    <BottomTab
      v-show="viewMode !== 'table'"
      ref="bottomTab"
      :orchestratorId="orchestratorId"
      :orchestratorVersionId="orchestratorVersionId"
      :flowId="flowId"
      :product="product"
      :readonly="readonly"
      @release="release"
    />
    <NodePath :data="json" :show="showNodePathPanel" @close="showNodePathPanel = false" @open-params="click" @open-node="dblclick" />
  </div>
</template>
<script>
import argument from './component/arguments.vue';
import resource from './component/resource.vue';
import nodeParameter from './component/nodeparameter.vue';
import vueProcess from '@dataspherestudio/shared/components/vue-process';
import console from './component/console.vue';
import api from '@dataspherestudio/shared/common/service/api';
import clickoutside from '@dataspherestudio/shared/common/helper/clickoutside';
import associateScript from './component/associateScript.vue';
import generateDatachecker from './component/generateDatachecker.vue';
import { throttle, debounce  } from 'lodash';
import { NODETYPE, ext } from '@/workflows/service/nodeType';
import storage from '@dataspherestudio/shared/common/helper/storage';
import mixin from '@dataspherestudio/shared/common/service/mixin';
import eventbus from "@dataspherestudio/shared/common/helper/eventbus";
import moment from 'moment';
import { getPublishStatus } from '@/workflows/service/api.js';
import module from './index';
import nodeIcons from './nodeicon';
import BottomTab from './component/bottomTab.vue';
import NodePath from './component/nodePath.vue';
import FlowDiffSubmit from './component/flowDiffSubmit.vue';
import FlowDiffPublish from './component/flowDiffPublish.vue';
import cyeditor from './cyeditor/index.vue'
import DesignToolbar from './component/designtoolbar.vue';
import { hasCycle } from './utils';
import { useData } from './component/useData.js';

const {
  getTemplateDatas,
} = useData();

export default {
  components: {
    vueProcess,
    argument,
    resource,
    nodeParameter,
    associateScript,
    generateDatachecker,
    console,
    BottomTab,
    NodePath,
    cyeditor,
    DesignToolbar,
    FlowDiffSubmit,
    FlowDiffPublish
  },
  mixins: [mixin],
  directives: {
    clickoutside,
  },
  props: {
    workspaceId: {
      type: [String, Number],
      default: ''
    },
    flowId: {
      type: [String, Number],
      default: '',
    },
    version: {
      type: [String, Number],
      default: '',
    },
    readonly: {
      type: [String, Boolean],
      default: false,
    },
    publish: {
      type: Boolean,
      default: false
    },
    product: {
      type: [Boolean],
      default: false,
    },
    importReplace: {
      type: Boolean,
      default: false,
    },
    openFiles: {
      type: Object,
      default: () => {},
    },
    tabs: {
      type: Array,
      default: () => [],
    },
    activeTabKey: {
      type: String
    },
    isLatest: {
      type: Boolean,
      default: true
    },
    orchestratorId: {
      type: [Number, String],
      default: null
    },
    orchestratorVersionId: {
      type: [Number, String],
      default: null
    },
    newTipVisible: {
      type: Boolean,
      default: false
    },
    flowStatus: {
      type: String,
      default: ''
    },
    associateGit: {
      type: Boolean,
      default: false
    },
    isMainFlow: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // 提交
      showSubmit: false,
      submitDesc: '',
      isFlowSubmit: false,
      // 发布前保存
      saveingComment: false,
      // 是否为父工作流
      isRootFlow: true,
      name: '',
      shapes: [],
      // 原始数据
      originalData: null,
      // 插件返回的json数据
      json: null,
      // 工作流级别的参数
      props: [
        {'user.to.proxy': ''}
      ],
      flowProxyUser: '',
      // 调度设置参数
      scheduleParams: {},
      // 工作流级别的资源
      resources: [],
      // 是否显示保存模态框
      saveModal: false,
      saveModel: {
        comment: '',
      },
      // 控制参数模态框是否显示
      isParamModalShow: false,
      isResourceShow: false,
      // 是否有改变
      jsonChange: false,
      loading: false,
      repetitionNameShow: false,
      repeatTitles: [],
      nodebaseinfoShow: false, // 自定义节点信息弹窗展示
      clickCurrentNode: {}, // 当前点击的节点
      viewOptions: {
        showBaseInfoOnAdd: false, // 不显示默认的拖拽添加节点弹出的基础信息面板
        shapeView: true, // 左侧shape列表
        control: true,
        linkType: 'straight' // straight：直线（直角折线）；curve：斜线
      },
      addNodeShow: false, // 创建节点的弹窗显示
      addEdgesShow: false, // 批量创建节点的弹窗显示
      addEdgesForm: {
        currentNode: '',
        currentNodeName: '',
        upstreamNodes: [],
        downstreamNodes: []
      },
      upstreamNodeList: [],
      downstreamNodeList: [],
      cacheNode: null,
      addNodeTitle: this.$t('message.workflow.process.createSubFlow'), // 创建节点时弹窗的title
      workflowIsExecutor: false, // 当前工作流是否再执行
      openningNode: null, // 上一次打开控制台的节点
      shapeWidth: 0, // 流程图插件左侧工具栏的宽度
      workflowExeteId: '',
      workflowTaskId: '',
      excuteTimer: '',
      executorStatusTimer: '',
      workflowExecutorCache: [],
      isDispatch: false,
      contextID: '',
      pubulishFlowComment: '',
      pubulishShow: false,
      publishFlowColumns:  [
          {
              title: '提交ID',
              key: 'commitId',
              minWidth: 220
          },
          {
              title: '提交时间',
              key: 'commitTime',
              minWidth: 160
          },
          {
              title: '提交人',
              key: 'commitUser',
              minWidth: 120
          },
          {
              title: '注释',
              key: 'comment',
              minWidth: 260
          }
      ],
      publishFlowData: [],
      flowVersion: '',
      isFlowPubulish: false,
      workflowExportShow: false,
      exportDesc: '',
      exporTChangeVersion: false,
      consoleParams: [],
      consoleHeight: 250,
      needReRun: false,
      locked: false,
      showNodePathPanel: false,
      iframeloading: false,
      isfullScreen: false,
      saveLoading: false, // 节点参数面板保存按钮
      viewMode: 'vueprocess' //  vueprocess, cyeditor or table
    };
  },
  computed: {
    getConsoleStyle() {
      return {
        'left': this.shapeWidth + 'px',
        'width': `calc(100% - ${this.shapeWidth}px)`
      }
    },
    // 获取新建节点时需要的参数列表
    createNodeParamsList() {
      return this.clickCurrentNode.nodeUiVOS ? this.clickCurrentNode.nodeUiVOS.filter((item) => item.baseInfo) : [];
    },
    formRules() {
      let rules = {};
      this.createNodeParamsList.map((item) => {
        rules[item.key] = this.paramsValid(item);
      })
      return rules;
    },
    showDispatchHistoryButton() {
      return this.$route.query.notPublish === 'true' || this.$route.query.notPublish === true
    },
    myReadonly() {
      return JSON.parse(this.readonly);
    },
    type() {
      return !this.isRootFlow ? 'subFlow' : 'flow'; // flow工作流， subFlow子工作流
    },
    nodeMenuOptions() {
      return {
        defaultMenu: {
          config: false, // 不展示默认的基础信息菜单项
          param: false, // 不展示默认的参数配置菜单项
          copy: false,
          delete: !this.workflowIsExecutor && !this.myReadonly
        },
        userMenu: [],
        beforeShowMenu: (node, arr, type) => {
          if (this.myReadonly) arr = []
          // type : 'node' | 'link' | 'view' 分别是节点右键，边右键，画布右键
          // 如果有runState说明已经执行过
          if (node && node.runState) {
            if (node.runState.showConsole && node.runState.taskID) {
              arr.push({
                text: this.$t('message.workflow.process.console'),
                value: 'console',
                icon: 'xitongguanlitai'
              })
            }
          }
          if (!this.workflowIsExecutor&& !this.myReadonly) {
            if (type === 'node') {
              if ([NODETYPE.SPARKSQL, NODETYPE.HQL, NODETYPE.SPARKPY, NODETYPE.SCALA, NODETYPE.NEBULA].includes(node.type)) {
                arr.push({
                  text: this.$t('message.workflow.process.associate'),
                  value: 'associate',
                  icon: 'associate', // 图标资源文件，也可以通过icon配置内置字体文件支持的className
                });
              }
              arr.push({
                text: this.$t('message.workflow.process.relySelect'),
                value: 'relySelect',
                icon: 'depselect',
                children: [{
                  text: this.$t('message.workflow.process.upstreamLevelOne'),
                  value: 'relySelectUpOne',
                  icon: 'depselect',
                }, {
                  text: this.$t('message.workflow.process.downstreamLevelOne'),
                  value: 'relySelectDownOne',
                  icon: 'depselect',
                }, {
                  text: this.$t('message.workflow.process.upAllLevel'),
                  value: 'relySelectUp',
                  icon: 'depselect',
                }, {
                  text: this.$t('message.workflow.process.downAllLevel'),
                  value: 'relySelectDown',
                  icon: 'depselect',
                }
                ]
              });
              // 通过节点类型去判断是否支持复制
              if (this.nodeCopy(node)) {
                arr.push({
                  text: this.$t('message.workflow.copy'),
                  value: 'mycopy',
                  icon: 'fuzhi',
                });
              }
              arr.push({
                value: 'addEdges',
                text: '批量关联节点',
                icon: 'addLink'
              })
              if ([NODETYPE.SPARKSQL, NODETYPE.HQL].includes(node.type)) {
                arr.push({
                  value: 'addDatachecker',
                  text: '生成Datachecker',
                  icon: 'icon-datacheck'
                })
              }
            }
          }
          if (type === 'view'&& !this.myReadonly) {
            arr.push({
              text: this.$t('message.workflow.paste'),
              value: 'mypaste',
              icon: 'zhantie',
            });
            arr.push({
              text: this.$t('message.workflow.process.allDelete'),
              value: 'allDelete',
              icon: 'delete'
            });
          }
          return arr;
        }
      }
    },
    tableViewUrl() {
      return `/next-web/#/workspace/workflow?workspaceId=${this.$route.query.workspaceId}&projectId=${this.$route.query.projectID}&flowId=${this.flowId}&labels=${this.getCurrentDsslabels()}`
    },
    isFlowSubmited() {
      return (this.associateGit && ['push', 'publish'].includes(this.flowStatus)) || !this.associateGit;
    }
  },
  created() {
    this.viewOptions.shapeView = !this.myReadonly;
  },
  watch: {
    jsonChange(val) {
      this.$emit('isChange', val);
    },
    workflowExecutorCache() {
      storage.set("workflowExecutorCache", this.workflowExecutorCache, 'local');
    }
  },
  mounted() {
    this.workflowExecutorCache = storage.get('workflowExecutorCache', 'local') || [];
    // 查找缓存中是否有当前工作流
    const currentExecutorFlow = this.workflowExecutorCache.filter((item) => item.flowId === this.flowId)
    if (currentExecutorFlow.length > 0) {
      this.workflowIsExecutor = true;
      this.queryWorkflowExecutor(currentExecutorFlow[0].execID, currentExecutorFlow[0].taskID)
      this.workflowExeteId = currentExecutorFlow[0].execID
      this.workflowTaskId = currentExecutorFlow[0].taskID
    }
    // 基础信息
    this.setShapes().then(() => {
      this.getBaseInfo();
    });
    this.shapeWidth = this.$refs.process && this.$refs.process.state.shapeOptions.viewWidth; // 自适应控制台宽度
    this.getConsoleParams();
    document.addEventListener('keyup', this.onKeyUp)
    eventbus.on('workflow.opennode.by.name', this.openNodeByName);
    eventbus.on('workflow.fold.left.tree', this.foldHandler);
    eventbus.on('workflow.copying', this.onCopying);
    window.addEventListener('message', this.msgEvent, false);
    window.addEventListener('resize', this.resizeConsole, false);
    this.checkSubmitStatus('init');
  },
  beforeDestroy() {
    if (this.timer) {
      clearInterval(this.timer);
    }
    if (this.excuteTimer) {
      clearTimeout(this.excuteTimer);
    }
    if (this.executorStatusTimer) {
      clearTimeout(this.executorStatusTimer);
    }
    if (this.updateLockTimer) {
      clearTimeout(this.updateLockTimer)
    }
    eventbus.off('workflow.fold.left.tree', this.foldHandler);
    eventbus.off('workflow.copying', this.onCopying);
    document.removeEventListener('keyup', this.onKeyUp);
    window.removeEventListener('message', this.msgEvent, false);
    window.removeEventListener('resize', this.resizeConsole, false);
  },
  methods: {
    // 获取当前节点对应的模板信息
    async getTemplateDataByProject(jobType) {
      const params = {
        projectId: this.$route.query.projectID,
        orchestratorId: this.$route.query.flowId,
        jobType,
      }
      const res = await getTemplateDatas(params);
      let templateList = [];
      res.forEach(e=> {
        e.child.forEach(d => {
          templateList.push(Object.assign(d))
        })
      });
      return templateList;
    },
    resizeConsole: debounce(function() {
      this.consoleHeight = this.$el ? this.$el.clientHeight / 2 : 250
    }, 300),
    msgEvent(e) {
      if (e.data) {
        try {
          let data = typeof e.data === 'string' ? JSON.parse(e.data) : e.data || {}
          if (data.type === 'dss-nextweb'&& data.flowId == this.flowId) {
            if (data.action === 'open_node') {
              this.dblclick(data.node)
            } else if(data.action === 'node_dependance') {
              this.showSearchPath()
            } else if(data.action === 'node_params') {
              this.click(data.node)
            }
          }
        } catch(err) {
          window.console.error(err)
        }
      }
    },
    foldHandler() {
      if (this.viewMode === 'vueprocess') {
        const refs = this.$refs
        refs.process && refs.process.layoutView()
      }
    },
    onCopying(data) {
      if (data.source.orchestratorId == this.orchestratorId) {
        this.locked = !data.done
      }
    },
    eventFromExt(evt) {
      if (evt && evt.callFn && typeof this[evt.callFn] === 'function') {
        this[evt.callFn](...evt.params)
      }
    },
    release(obj) {
      this.$emit('release', obj);
    },
    // 保存node参数修改
    saveNodeParameter() {
      this.$refs.nodeParameter.save();
    },
    saveButtonStatus(loading) {
      this.saveLoading = loading
    },
    // 右键判断是否支持复制
    nodeCopy(node) {
      let flag = false;
      this.shapes.forEach((item) => {
        if (item.children) {
          item.children.forEach((subItem) => {
            if (subItem.type === node.type) {
              flag = subItem.enableCopy;
              return;
            }
          })
        }
      })
      return flag;
    },
    // 各参数的校验方法
    paramsValid(param) {
      // 自定义函数的方法先写这里
      const validatorTitle = (rule, value, callback) => {
        if (value === `${this.name}`) {
          callback(new Error(rule.message));
        } else {
          callback();
        }
      }
      let temRule = [];
      if (param.nodeUiValidateVOS) {
        param.nodeUiValidateVOS.map((item) => {
          // 如果是正则类型的就写成正则
          if (item.validateType === 'Required') {
            temRule.push({
              required: true,
              message: item.message,
              trigger: item.trigger,
              type: ['MultiBinding', 'MultiSelect'].includes(param.uiType) ? 'array' : 'string'
            })
          } else if (item.validateType === 'Regex') {
            temRule.push({
              type: 'string',
              pattern: new RegExp(item.validateRange),
              message: item.message,
              trigger: item.trigger
            })
          } else if (item.validateType === 'Function') {
            temRule.push({
              validator: ['validatorTitle'].includes(item.validateRange) ? validatorTitle : () => {},
              trigger: 'blur'
            })
          }
        })
      }
      return temRule;
    },
    // 根据返回的添加去获取需要绑定的列表
    conditionBindList(param) {
      let temArry = [];
      if (param.defaultValue === 'empty') {
        temArry.push({
          name: this.$t('message.workflow.process.notBinding'),
          key: 'empty'
        })
      }
      // 对绑定的参数进行过滤
      const conditionResult = (type) => {
        if (param.value && JSON.parse(param.value)) {
          // 如果是通配符就返回true
          const optionsList = JSON.parse(param.value);
          if (optionsList[0] === '*') {
            return true;
          } else {
            return optionsList.includes(type);
          }
        }
      }
      if (this.json.nodes && this.json.nodes.length) {
        this.json.nodes.forEach((node) => {
          if (node.key !== this.clickCurrentNode.key && conditionResult(node.type)) {
          // 当sql节点里面没内容时,resources属性值为[]，这种sql节点不放做选项
            const tempObj = {
              name: node.title,
              key: node.key,
            }
            temArry.push(tempObj)
          }
        })
      }
      return temArry;
    },
    filterFormType(val) {
      switch (val) {
        case 'Text':
          return 'textarea';
        default:
          return 'text';
      }
    },
    updateOriginData(node, scriptisSave) {
      this.json.nodes = this.json.nodes.map((item) => {
        if (item.key === node.key) {
          item.jobContent = node.jobContent;
          item.resources = node.resources;
          item.params = node.params;
          item.modifyUser = this.getUserName();
          item.modifyTime = Date.now();
        }
        return item;
      })
      // 避免在保存脚本时，已打开右侧参数栏，此时保存的会是旧值
      if (this.clickCurrentNode && this.clickCurrentNode.key === node.key) {
        this.clickCurrentNode.jobContent = node.jobContent;
        this.clickCurrentNode.resources = node.resources;
        this.clickCurrentNode.params = node.params;
      }
      this.originalData = this.json;
      // 更新节点之后自动保存json
      if (scriptisSave) {
        this.autoSave(this.$t('message.workflow.Save'), false);
      }
    },
    urlContainsParams(paramsToCheck) {
      // 获取当前URL
      let url = new URL(window.location.href);
      // window.console.log('Current URL:', url.toString());

      // 检查查询字符串部分
      let searchParams = new URLSearchParams(url.search);

      // 将要检查的参数转换为数组
      if (typeof paramsToCheck === 'string') {
          paramsToCheck = [paramsToCheck];
      }

      // 用于存储哪些参数被找到
      let foundParams = [];

      // 遍历需要检查的参数列表
      paramsToCheck.forEach(param => {
          if (searchParams.has(param)) {
              foundParams.push(param);
              // window.console.log(`Parameter "${param}" found in query string.`);
          }
      });

      // 获取并解析哈希部分
      let hash = window.location.hash.slice(1); // 去掉开头的 #
      if (hash) {
          // 使用占位符来创建一个有效的URL以解析哈希部分
          let hashUrl = new URL(`http://placeholder.com/?${hash}`);
          let hashSearchParams = new URLSearchParams(hashUrl.search);

          // 再次遍历需要检查的参数列表，这次是针对哈希部分
          paramsToCheck.forEach(param => {
              if (!foundParams.includes(param) && hashSearchParams.has(param)) {
                  foundParams.push(param);
                  // window.console.log(`Parameter "${param}" found in hash.`);
              }
          });
      }

      // 如果所有要检查的参数都被找到了
      if (foundParams.length === paramsToCheck.length) {
          // window.console.log('All specified parameters are present.');
          return true;
      } else {
          window.console.log('Some or none of the specified parameters are present.');
          return false;
      }
    },
    getBaseInfo() {
      this.loading = true;
      this.clickCurrentNode = {};
      this.nodebaseinfoShow = false;
      this.getOriginJson();
      // 自动打开子工作流
      let containsParams = this.urlContainsParams(['appId']);
      if(containsParams && this.$route.query.appId && this.flowId !== Number(this.$route.query.appId)) {
        api.fetch(`/dss/workflow/get`, {
          flowId: Number(this.$route.query.appId),
          labels: this.getCurrentDsslabels()
        },'get').then((res) => {
          const arg = {
          appId: Number(this.$route.query.appId),     // (跳转节点所属)直接工作流id
          flowId: Number(this.$route.query.flowId),  // 顶层工作流id
          flowName: res.flow.name || '', // 直接工作流名称
          flowNodeId: res.flow.resourceId || '', // 直接工作流节点id
          jumpNodeName: this.$route.query.jumpNodeName, // 跳转节点名称
        }
        this.$emit('open-subFlow', arg);
        }).catch((err) => {
          window.console.error(err);
        })
      }
    },
    initAction(json) {
      // 创建工作流之后就有值
      this.contextID = json.contextID;
      // 保存节点才有的值
      this.schedulerAppConnName = json.schedulerAppConnName
      if (json) {
        if (json.nodes) {
          this.originalData = this.json = JSON.parse(JSON.stringify(json));
          this.resources = json.resources;
        }
        if (json.props) {
          this.props = json.props;
          this.flowProxyUser = (json.props[0] || {})['user.to.proxy'];
          this.scheduleParams = json.scheduleParams || {};
        }
      }
      if (json.config && json.config.type != 'table') {
        this.viewMode = json.config.type
      }
      this.pollUpdateLock();
      this.$nextTick(() => {
        this.loading = false;
        let containsParams = this.urlContainsParams(['appId', 'nodeName']);
        if (containsParams &&  this.$route.query.origin === 'gitSearch') {
          this.openSubNodeByName()
        } else {
          this.openNodeByName()
        }
      })
    },
    openSubNodeByName() {
      if (!this.originalData) return
      let nodeName = this.$route.query.nodeName;
      const node = this.originalData.nodes.find(node => {
        return node.title === nodeName
      })
      if (node) {
        this.dblclick(node)
      }
      setTimeout(() => {
        storage.remove('openflownode');
      }, 2500)
    },
    openNodeByName(params) {
      if (!this.originalData) return
      const openflownode = storage.get('openflownode');
      let nodeName;
      let flowId;
      if (params) {
        flowId = params.flowId
        nodeName = params.nodeName
      } else if (openflownode && !params) {
        // 查找工作流内容新窗口打开节点的情况
        params = openflownode.split('_flowidname_')
        flowId = params[0] 
        nodeName = params[1] 
      }
      const node = this.originalData.nodes.find(node => {
        return node.title === nodeName
      })
      if (flowId == this.$route.query.flowId && node) {
        this.dblclick(node)
      }
      setTimeout(() => {
        storage.remove('openflownode');
      }, 2500)
    },
    getOriginJson() {
      return api.fetch(`/dss/workflow/get`, {
        flowId: this.flowId,
        labels: this.getCurrentDsslabels()
      },'get').then((res) => {
        let json = this.convertJson(res.flow);
        let flowEditLock = res.flow.flowEditLock;
        if (flowEditLock && !this.myReadonly) {
          this.setFlowEditLock(flowEditLock);
        }
        if (json) {
          this.initAction(json);
        } else {
          this.loading = false;
        }
      }).catch((err) => {
        window.console.error(err);
        this.loading = false;
        this.locked = true;
        if (this.updateLockTimer) {
          clearTimeout(this.updateLockTimer)
        }
        this.$emit('close')
      });
    },
    convertJson(flow) {
      this.name = flow.name;
      this.isRootFlow = flow.rootFlow;
      this.rank = flow.rank; // 工作流层级
      let json;
      json = flow.flowJson;

      if (json) {
        json = JSON.parse(json);
        if (json.nodes && Array.isArray(json.nodes)) {
          json.nodes = json.nodes.map((node) => {
            node.disabled = false;
            if (node.params && node.params.configuration && node.params.configuration.special) {
              node.disabled = node.params.configuration.special['auto.disabled'] === 'true';
            }
            node.type = node.jobType;
            delete node.jobType;
            return node;
          });
        }
        this.orcVersion = json.orcVersion
        // 代理用户有默认值做默认赋值
        if(flow.defaultProxyUser) {
          if(!json.scheduleParams) {
            json.scheduleParams = {}
            json.scheduleParams.proxyuser = flow.defaultProxyUser
          }
          if(!json.props) {
            json.props= [{
              'user.to.proxy': flow.defaultProxyUser,
            }]
          }
        }
      }
      return json;
    },
    setShapes() {
      return api.fetch(`${this.$API_PATH.WORKFLOW_PATH}listNodeType`, {
        labels: this.getCurrentDsslabels()
      }, {
        method: 'get',
        cacheOptions: { time: 2 * 60 * 1000 },
      }).then((res) => {
        this.shapes = res.nodeTypes.map((item) => {
          if (item.children.length > 0) {
            item.children = item.children.map((subItem) => {
              // svg绘制的点太多，导致动画卡顿，使用图片代替
              if (nodeIcons[subItem.title]) {
                subItem.image = nodeIcons[subItem.title];
              } else if (subItem.image) {
                subItem.image = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(subItem.image)))
              } else {
                subItem.image = `/api/rest_j/v1/dss/workflow/nodeIcon/${subItem.type}`
              }
              return subItem
            })
          }
          return item;
        });
      });
    },
    change(obj) {
      if (!obj) return
      const change = this.checkChange(obj)
      this.json = obj;
      if (change) {
        this.heartBeat();
        this.jsonChange = true;
      }
    },
    checkChange(obj) {
      // 节点增删,连线增删视为发生改变
      return this.json ? obj.edges.length != this.json.edges.length ||  obj.nodes.length != this.json.nodes.length : true
    },
    initNode(arg) {
      if(this.clickCurrentNode.id && this.clickCurrentNode.id === arg.id) return; // 多出点击时，避免数据初始化
      arg = this.bindNodeBasicInfo(arg);
      this.clickCurrentNode = JSON.parse(JSON.stringify(arg));
    },
    click(arg) {
      clearTimeout(this.timerClick);
      this.timerClick = setTimeout(() => {
        if (this.workflowIsExecutor) return;
        this.nodebaseinfoShow = true;
        this.initNode(arg);
        this.$emit('node-click', arg);
      }, 200);
    },
    dblclick(...arg) {
      if (this.lastDblClickTime && Date.now() - this.lastDblClickTime < 600) {
        return; // 防止双击事件触发多次
      }
      this.lastDblClickTime = Date.now();
      arg[0] = this.bindNodeBasicInfo(arg[0]);
      arg[0].contextID = this.contextID;
      // 由后台控制是否支持跳转
      clearTimeout(this.timerClick);
      // 执行过程中只有子工作流可双击打开
      if (!arg[0].supportJump || (this.workflowIsExecutor && arg[0].type !== NODETYPE.FLOW)) return;
      if ((!arg[0].jobContent || Object.keys(arg[0].jobContent).length === 0) && arg[0].shouldCreationBeforeNode) {
        this.addNodeShow = true;
        this.clickCurrentNode = JSON.parse(JSON.stringify(arg[0]));
        this.addNodeTitle = this.$t('message.workflow.process.createNode');
      } else {
        // 为node信息添加modelType字段方便脚本格式判断
        arg[0].modelType = ext[arg[0].type];
        // dpms  /product/100199/story/detail/365928 配置了模板则传递参数时根据配置过滤
        if (arg[0].params && arg[0].params.configuration && arg[0].params.configuration.startup["ec.conf.templateId"]) {
          arg[0].nodeUiVOS.forEach((item) => {
            let show = this.$refs.nodeParameter.checkShow(item, arg[0]);
            if (show === false) {
              if (typeof item.condition === 'string' && item.condition && item.condition.indexOf('configuration.startup') > -1) {
                delete arg[0].params.configuration.startup[item.key];
              }
              if (typeof item.condition === 'string' && item.condition && item.condition.indexOf('configuration.runtime') > -1) {
                delete arg[0].params.configuration.runtime[item.key];
              }
            }
          })
        }
        if (arg[0].params && arg[0].params.configuration && arg[0].params.configuration.startup) {
          delete arg[0].params.configuration.startup['wds.linkis.rm.yarnqueue']
        }
        this.$emit('node-dblclick', arg);
      }
    },
    message(obj) {
      let type = {
        warning: 'warn',
        error: 'error',
        info: 'info',
      };
      this.$Message[type[obj.type]]({
        content: obj.msg,
        duration: 2,
      });
    },
    async saveNodeBaseInfo(arg) {
      if (this.lastSaveTime && new Date().getTime() - this.lastSaveTime < 2000) {
        return
      } else {
        this.lastSaveTime = new Date().getTime();
      }
      this.$emit('saveBaseInfo', arg);
      // 如果是可编辑脚本得改变打开的脚本得名称
      this.dispatch('Workbench:updateFlowsNodeName', arg);
      // 当保存子流程节点的基础信息时，如果子流程节点没有 embeddedFlowId:"flow_id" 则先创建子流程节点
      let node = arg;
      if (node.type == NODETYPE.FLOW) {
        if (this.rank >= 4) {
          return this.$Message.warning(this.$t('message.workflow.process.rankLimit'));
        }

        //  节点原始数据
        if (!node.jobContent) {
          node.jobContent = {};
        }
        // 如果子流程节点的node.jobContent.embeddedFlowId为空表明还未子流程还未创建生成flowID
        const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/;
        if (!node.title.match(reg)) {
          return this.$Message.warning(this.$t('message.workflow.validNameDesc'));
        }

        if (!node.jobContent.embeddedFlowId) {
          // 调用接口创建
          const result = await api.fetch(`${this.$API_PATH.WORKFLOW_PATH}addFlow`, {
            name: node.title,
            description: node.desc,
            parentFlowID: Number(this.flowId),
            workspaceName: this.getCurrentWorkspaceName(),
            projectName: this.$route.query.projectName,
            version: this.version,
            userName: this.getUserName(),
            labels: {route: this.getCurrentDsslabels()},

          }).then((res) => {
            this.$Notice.success({
              desc: this.$t('message.workflow.process.createSubSuccess'),
            });
            node.jobContent.embeddedFlowId = res.flow.id;
            return true
          });
          if (result !== true) {
            return
          }
        } else {
          await api.fetch(`${this.$API_PATH.WORKFLOW_PATH}updateFlowBaseInfo`, {
            id: node.jobContent.embeddedFlowId,
            name: node.title,
            description: node.desc,
            labels: {route: this.getCurrentDsslabels()}
          }, 'post').then(() => {
          })
        }
      } else {
        // iframe节点
        await this.saveCommonIframe(node);
      }

      // 为了表单校验，基础信息弹窗保存的节点已不再是响应式，需重新赋值给json

      this.json.nodes = this.json.nodes.map((item) => {
        if (item.key === node.key) {
          item.title = node.title;
          item.desc = node.desc;
          item.ecConfTemplateName = node.ecConfTemplateName;
          item.ecConfTemplateId = node.ecConfTemplateId;
          item.jobContent = node.jobContent;
          item.resources = node.resources || [];
          item.params = node.params; // 节点参数现在存在这里，和jobparams一样
          item.appTag = node.appTag;
          item.businessTag = node.businessTag;
          item.modifyUser = this.getUserName();
          item.modifyTime = Date.now();
          item.disabled = false;
          if (item.params && node.params.configuration && node.params.configuration.special) {
            item.disabled = node.params.configuration.special['auto.disabled'] === 'true';
          }
        }
        item.selected = item.key === node.key
        return item;
      });
      this.originalData = {...this.json};
      this.jsonChange = true;
      this.addNodeShow = false;
      // 保存工作流
      this.autoSave('paramsSave', false);
      // 表格模式更新
      if (this.viewMode === 'table') {
        const ifr = this.$refs.ifr;
        if (ifr) {
          setTimeout(()=> {
            ifr.contentWindow.postMessage(JSON.stringify({
              type: 'dss_change_node'
            }), "*");
          }, 600)
        }
      }
    },
    /**
     * 保存工作流
     */
    handleSave: debounce(function () {
      this.save()
    }, 1500),
    save() {
      if (this.workflowIsExecutor) return;
      // 检查JSON
      if (!this.validateJSON()) {
        return;
      }
      // 检查当前json是否有子节点未保存
      const subArray = this.openFiles[this.name] || [];
      const changeList = this.tabs.filter((item) => {
        return subArray.includes(item.key) && item.node.isChange;
      });
        // 保存时关闭控制台
      this.openningNode = null;
      if (changeList.length > 0) {
        this.$Modal.confirm({
          title: this.$t('message.workflow.process.cancelNotice'),
          content: this.$t('message.workflow.process.noSaveHtml'),
          okText: this.$t('message.workflow.process.confirmSave'),
          cancelText: this.$t('message.workflow.cancel'),
          onOk: () => {
            this.saveModal = false;
            let json = JSON.parse(JSON.stringify(this.json));
            if (this.viewMode !== 'table' && this.$refs.process) {
              json.nodes.forEach((node) => {
                this.$refs.process.setNodeRunState(node.key, {
                  borderColor: '#6A85A7',
                })
              })
            }
            this.autoSave(this.$t('message.workflow.Manually'), false);
          },
          onCancel: () => {
          },
        });
      } else {
        this.saveModal = false;
        let json = JSON.parse(JSON.stringify(this.json));
        if (this.viewMode !== 'table' && this.$refs.process) {
          json.nodes.forEach((node) => {
            this.$refs.process.setNodeRunState(node.key, {
              borderColor: '#6A85A7',
            })
          })
        }
        this.autoSave(this.$t('message.workflow.Manually'), false);
      }
    },
    autoSave(comment, f) {
      // 检查JSON
      if (!this.validateJSON()) {
        this.loading = false;
        return false;
      }
      if (this.viewMode === 'cyeditor' && comment === 'deleteSave') {
        this.originalData = this.json;
      }
      let json = JSON.parse(JSON.stringify(this.json));
      let flage = false;
      // 节点连线保存数据key白名单
      json.nodes =  json.nodes.map((node) => {
        const keys = [
          'ecConfTemplateId',
          'ecConfTemplateName',
          'jobContent',
          'key',
          'title',
          'desc',
          'layout',
          'params',
          'resources',
          'createTime',
          'modifyTime',
          'modifyUser',
          'id',
          'jobType',
          'businessTag',
          'type',
          'appTag'
        ]
        const data = {}
        keys.forEach(it => {
          data[it] = node[it]
        })
        const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/;

        if (!node.title.match(reg)) {
          return flage = true;
        }
        data.id = data.key;
        data.jobType = data.type;
        delete data.type;
        // 将用户保存的resources值为空字符串转为空数组
        if (!data.resources) {
          data.resources = [];
        }
        return data
      });
      // 拖拽模式保存
      json.config = {
        ...json.config,
        type: this.viewMode === 'table' ? this.preDragViewMode || 'vueprocess' : this.viewMode
      }
      if (flage) return this.$Message.warning(this.$t('message.workflow.validNameDesc'));
      const isFiveNode = json.nodes.filter((item) => {
        return !item.jobContent && item.jobType === NODETYPE.FLOW && this.rank >= 4;
      });
      if (isFiveNode.length > 0) return this.$Message.warning(this.$t('message.workflow.process.deleteNodeSave'));
      return this.saveRequest(json, comment, f);
    },
    // 保存请求
    saveRequest(json, comment, f) {
      const updateTime = Date.now();
      const paramsJson = JSON.parse(JSON.stringify(Object.assign(json, {
        comment: comment,
        type: this.type,
        updateTime,
        updateUser: this.getUserName(),
        props: this.props,
        resources: this.resources,
        scheduleParams: this.scheduleParams,
        contextID: this.contextID,
        orcVersion: this.orcVersion
      })));
      if (this.schedulerAppConnName !== undefined) {
        paramsJson.schedulerAppConnName = this.schedulerAppConnName
      }
      return api.fetch(`${this.$API_PATH.WORKFLOW_PATH}saveFlow`, {
        id: Number(this.flowId),
        json: JSON.stringify(paramsJson),
        projectName: this.$route.query.projectName,
        workspaceName: this.getCurrentWorkspaceName(),
        labels: {
          route: this.getCurrentDsslabels()
        },
        flowEditLock: this.getFlowEditLock()
      }).then((res) => {
        this.loading = false;
        // 将更新的互斥锁的res.flowEditLock字段存储到本地
        let flowEditLock = res.flowEditLock;
        if (flowEditLock) {
          this.setFlowEditLock(flowEditLock)
          this.pollUpdateLock()
        }
        if (!f) {
          this.$Notice.success({
            desc: this.$t('message.workflow.process.saveWorkflowCuccess'),
          });
          this.saveModel.comment = '';
        } else {
          this.$Notice.success({
            desc: this.$t('message.workflow.process.autoSaveWorkflow'),
          });
        }
        this.jsonChange = false; 
        if(this.props && this.props.length > 0) {
          this.flowProxyUser = this.props[0]['user.to.proxy'];
        }
        // 保存成功后去更新tab的工作流数据
        this.$emit('updateWorkflowList');
        if(!this.isFlowSubmit && !this.isFlowPubulish) {
          this.$emit('updateFlowStatus');
        }
        return res;
      }).catch((e) => {
        window.console.error('saveRequest',e)
        this.loading = false;
      });
    },
    /**
     * 显示工作流参数配置页面
     */
    showParamView() {
      if (this.workflowIsExecutor) return;
      this.nodebaseinfoShow = false;
      if (!this.isResourceShow && !this.isDispatch) {
        this.isParamModalShow = !this.isParamModalShow;
      } else {
        this.isParamModalShow = true;
        this.isResourceShow = false;
        this.isDispatch = false;
      }
    },
    changeLinkType(type) {
      this.viewOptions = {
        ...this.viewOptions,
        linkType: type
      }
    },
    changeLinkStraight(k) {
      this.$refs.process.changeLinkType(k, 'straight')
    },
    changeLinkCurve(k) {
      this.$refs.process.changeLinkType(k, 'curve')
    },
    setFlowEditLock(flowEditLock) {
      let data = storage.get("flowEditLock") || {}
      const key = this.getUserName()
      const updateList = (data[key] || []).filter(it => it.projectId != this.$route.query.projectID)
      updateList.push({ flowId: this.flowId, lock: flowEditLock, projectId: this.$route.query.projectID })
      storage.set("flowEditLock", {
        [key]: updateList
      });
    },
    getFlowEditLock() {
      const data = storage.get("flowEditLock") || {}
      const key = this.getUserName()
      const item = (data[key] || []).find(it => it.flowId == this.flowId && it.projectId == this.$route.query.projectID)
      return item && item.lock
    },
    updateLock() {
      const flowEditLock = this.getFlowEditLock()
      if (!flowEditLock || this.myReadonly) return
      api.fetch(`${this.$API_PATH.WORKFLOW_PATH}updateFlowEditLock`, {
        flowEditLock,
        labels: this.getCurrentDsslabels()
      }, 'get')
        .then((rst) => {
          if (rst.flowEditLock) {
            this.setFlowEditLock(rst.flowEditLock)
            this.pollUpdateLock()
          }
        }).catch(()=>{
          //
        })
    },
    pollUpdateLock() {
      clearTimeout(this.updateLockTimer)
      this.updateLockTimer = setTimeout(()=>{
        this.updateLock()
      }, 2 * 60 * 1000)
    },
    /**
     * 显示资源导入页面
     */
    showResourceView() {
      if (this.workflowIsExecutor) return;
      this.isDispatch = false;
      this.nodebaseinfoShow = false;
      if (this.isResourceShow) {
        this.isParamModalShow = !this.isParamModalShow;
      } else {
        this.isParamModalShow = true;
        this.isResourceShow = true;
      }
    },
    /**
     * 检查JSON，是否符合规范
     * @return {Boolean}
     */
    validateJSON() {
      if (!this.json) {
        this.$Modal.warning({
          title: this.$t('message.workflow.process.notice'),
          content: this.$t('message.workflow.process.dragNode'),
        });
        return false;
      }
      let edges = this.json.edges;
      let nodes = this.json.nodes;
      if (!nodes || nodes.length === 0) {
        this.$Modal.warning({
          title: this.$t('message.workflow.process.notice'),
          content: this.$t('message.workflow.process.dragNode'),
        });
        return false;
      }
      let headers = [];
      let footers = [];
      let titles = [];
      let repeatTitles = [];
      nodes.forEach((node) => {
        if (titles.includes(node.title)) {
          repeatTitles.push(node.title);
        } else {
          titles.push(node.title);
        }
        if (!edges.some((edge) => {
          return edge.target == node.key;
        })) {
          headers.push(node);
        }
        if (!edges.some((edge) => {
          return edge.source == node.key;
        })) {
          footers.push(node);
        }
      });
      // 后台会把名称当做id处理，所以名称必须唯一
      if (repeatTitles.length > 0) {
        this.repeatTitles = repeatTitles;
        this.repetitionNameShow = true;
        return false;
      }
      return true;
    },
    onPropsChange(value, proxyUser, proxyUserChange) {
      if (proxyUserChange) {
        api.fetch('/dss/framework/workspace/isDismissed', {usernames: [proxyUser]}, 'post').then(rst => {
          if (rst && (rst.isDismissed || []).some(item => Object.values(item)[0])) {
            this.$Message.warning('代理用户已离职或不存在的用户');
          } else {
            this.jsonChange = true;
            this.props = value;
          }
        })
      } else {
        this.jsonChange = true;
        this.props = value;
      }
    },
    onScheduleChange(value) {
      this.scheduleParams = value;
    },
    handleOutsideClick(e) {
      let paramButton = this.$refs.paramButton;
      let resourceButton = this.$refs.resourceButton;
      let dispatchButton = this.$refs.dispatchButton;
      if ((paramButton && paramButton.contains(e.target)) || e.target === paramButton
                || (resourceButton && resourceButton.contains(e.target)) || e.target === resourceButton || (dispatchButton && dispatchButton.contains(e.target)) || e.target === dispatchButton) {
        return;
      }
      if (this.isParamModalShow) {
        this.isParamModalShow = false;
      }
    },
    handleOutsideClickNode(e) {
      if (e.target.className === 'node-box-content' || e.target.className === 'node-box') return;
      if (this.nodebaseinfoShow) {
        this.nodebaseinfoShow = false;
      }
    },
    updateResources(res) {
      this.resources = res.map((item) => {
        return {
          fileName: item.fileName,
          resourceId: item.resourceId,
          version: item.version,
        };
      });
      this.jsonChange = true;
      this.autoSave('更新资源文件', false)
    },
    async nodeDelete(node) {
      // 正在执行中的节点不能被删除
      if (node && node.runState && node.runState.status === 1) {
        return;
      }
      node = this.bindNodeBasicInfo(node);
      if (node.type === NODETYPE.FLOW && node.jobContent && node.jobContent.embeddedFlowId) {
        const params = {
          id: +node.jobContent.embeddedFlowId,
          sure: false,
          labels: {
            route: this.getCurrentDsslabels()
          }
        }
        await api.fetch(`${this.$API_PATH.WORKFLOW_PATH}deleteFlow`, params, 'post').then(() => {
          this.$Message.success(this.$t('message.workflow.deleteSuccess'));
          this.$emit('deleteNode', node);
          if (this.$refs.process.deleteNode) {
            this.$refs.process.deleteNode(node.key)
          }
          // 如果删除的是当前修改参数的节点，关闭侧边栏
          if (this.clickCurrentNode.key === node.key) {
            this.clickCurrentNode = {};
            this.nodebaseinfoShow = false;
          }

          // 删除事件比jsonchange时机早
          const timeId = setTimeout(() => {
            this.autoSave('deleteSave', false);
            clearTimeout(timeId);
          }, 500)
        })
      } else {
        if (node.jumpType == 1) {
          const params = {
            nodeType: node.type,
            name: node.title,
            description: node.desc,
            projectID: +this.$route.query.projectID,
            params: node.jobContent,
            labels: {
              route: this.getCurrentDsslabels()
            }
          }
          await api.fetch(`${this.$API_PATH.WORKFLOW_PATH}deleteAppConnNode`,params, 'post').then(() => {
            this.$Message.success(this.$t('message.workflow.deleteSuccess'));
            this.$emit('deleteNode', node);
            if (this.$refs.process.deleteNode) {
              this.$refs.process.deleteNode(node.key)
            }

            // 如果删除的是当前修改参数的节点，关闭侧边栏
            if (this.clickCurrentNode.key === node.key) {
              this.clickCurrentNode = {};
              this.nodebaseinfoShow = false;
            }

            // 删除事件比jsonchange时机早
            const timeId = setTimeout(() => {
              this.autoSave('deleteSave', false);
              clearTimeout(timeId);
            }, 500)
          })
        } else {
          this.$emit('deleteNode', node);
          if (this.$refs.process.deleteNode) {
            this.$refs.process.deleteNode(node.key);
          }
          // 如果删除的是当前修改参数的节点，关闭侧边栏
          if (this.clickCurrentNode.key === node.key) {
            this.clickCurrentNode = {};
            this.nodebaseinfoShow = false;
          }

          // 删除事件比jsonchange时机早
          const timeId = setTimeout(() => {
            this.autoSave('deleteSave', false);
            clearTimeout(timeId);
          }, 500)
        }
      }
    },
    repetitionName() {
      this.repetitionNameShow = false;
    },
    // 单击节点出来的右边的弹框的保存事件
    saveNode(node) { // 保存节点参数配置
      const nodeItem = this.json.nodes.find(item => {
        return item.id === node.id
      })
      if (nodeItem && nodeItem.params && nodeItem.params.configuration && nodeItem.params.configuration.startup && node.params && node.params.configuration && node.params.configuration.startup) {
        let hasChange = false;
        for (let key in nodeItem.params.configuration.startup) {
          if (key !== 'wds.linkis.rm.yarnqueue' && node.params.configuration.startup[key] !== nodeItem.params.configuration.startup[key]) {
            hasChange = true;
          }
        }
        if (hasChange) {
          this.$Modal.confirm({ title: '提示', content: '请注意引擎参数有修改，若节点已打开，请关闭后重新打开，同时Kill引擎方才生效！' });
        }
      }
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.readonly'));
      this.saveNodeBaseInfo(node);
    },
    onContextMenu(menu, data, type) {
      switch(menu) {
        case 'associate':
          this.checkAssociated(data);
          break;
        case 'console':
          this.openConsole(data);
          break;
        case 'mycopy':
          this.copyNode(data);
          break;
        case 'mypaste':
          this.pasteNode(data);
          break;
        case 'allDelete':
          this.allDelete();
          break;
        case 'relySelectUpOne':
          this.relySelect(data, 'up-one');
          break;
        case 'relySelectDownOne':
          this.relySelect(data, 'down-one');
          break;
        case 'relySelectUp':
          this.relySelect(data, 'up');
          break;
        case 'relySelectDown':
          this.relySelect(data, 'down');
          break;
        case 'addDatachecker':
          this.addDatachecker(data);
          break;
        case 'delete':
          if (this.viewMode === 'cyeditor') {
            if (type ==='node') {
              this.nodeDelete(data)
            } else if(type === 'link') {
              this.linkDelete(data)
            }
          }
          break;
        case 'addEdges':
          this.beforeAddEdges(data);
          break;
      }
    },
    getNoBindNode(id) {
      const upstreamIds = this.json.edges.filter(item => item.target === id).map(item => item.source);
      const downstreamIds = this.json.edges.filter(item => item.source === id).map(item => item.target);
      const ids = [ id, ...upstreamIds, ...downstreamIds ];
      const results = this.json.nodes.filter(item => !ids.includes(item.id || item.key));
      return results;
    },
    addDatachecker(node, data){
      if (data && data.length) {
        // 第一行库表放到check.object，其余行放到job.desc
        let checkObject = `${data[0].db}.${data[0].table}`;
        if(data[0].partition) {
          checkObject += `{${data[0].partition}}`;
        }
        const jobDesc = data.slice(1).map((item,idx) => {
          return item.partition ? `check.object.${idx+1}=${item.db}.${item.table}{${item.partition}}` : `check.object.${idx+1}=${item.db}.${item.table}`;
        }).join('\n')
        // 添加datacheck节点及连线
        const checkerNode = {
          "type": "linkis.appconn.datachecker",
          "title": `datachecker_${Math.floor(Math.random()*10000)}`,
          "desc": "",
          "image": "/api/rest_j/v1/dss/workflow/nodeIcon/linkis.appconn.datachecker",
          "key": Date.now(),
          "layout": {
            "width": 150,
            "height": 40,
            "x": node.x - Math.random() * 50 - 150 > 100 ? node.x - Math.random() * 50 - 150 : 100,
            "y": node.y - Math.random() * 30 - 60 > 100 ? node.y - Math.random() * 30 - 60 : 100,
          },
          params:{ 
            configuration:  {
              special: {},
              runtime: {
                'check.object': checkObject,
                'job.desc': jobDesc
              },
              startup: {}
            }
          },
          "selected": true,
          "createTime": Date.now()
        }
        const edge = {
          linkType: "straight",
          target: node.key,
          source: checkerNode.key,
          sourceLocation: "bottom",
          targetLocation: "top"
        }
        this.json.edges.push(edge);
        this.json.nodes.push(checkerNode);
        this.originalData = { ...this.json };
        this.jsonChange = true;
      } else {
        this.$refs.datachecker.open(node);
      }
    },
    beforeAddEdges(node) {
      this.addEdgesShow = true;
      this.addEdgesForm.currentNode = node.id || node.key;
      this.addEdgesForm.currentNodeName = node.title;
      const list = this.getNoBindNode(node.id || node.key)
      list.forEach((item) => {
        item.key = item.id || item.key;
      });
      this.upstreamNodeList = list;
      this.downstreamNodeList = list;
    },
    async addEdges() {
      const { currentNode, upstreamNodes, downstreamNodes } = this.addEdgesForm;
      const newEdges = [ ...this.json.edges ];
      upstreamNodes.forEach(item => {
        newEdges.push({
          source: item,
          target: currentNode,
        })
      });
      downstreamNodes.forEach(item => {
        newEdges.push({
          source: currentNode,
          target: item,
        })
      });
      if(hasCycle(newEdges)) {
        this.$Message['warning']({
          content: '关联节点上下游节点存在闭环',
          duration: 2,
        });
        return;
      }
      this.json.edges = [ ...newEdges ];
      this.autoSave('addEdges', false);
      this.originalData = { ...this.json };
      this.cancelEdges(false);
    },
    cancelEdges(val) {
      if (!val) {
        this.addEdgesShow = false;
        this.addEdgesForm = {
          currentNode: '',
          currentNodeName: '',
          upstreamNodes: [],
          downstreamNodes: []
        }
        this.upstreamNodeList = [];
        this.downstreamNodeList = [];
      }
    },
    changeNodes(type) {
      const { currentNode, upstreamNodes, downstreamNodes } = this.addEdgesForm;
      const list = this.getNoBindNode(currentNode);
      if (type === 'upstream') {
        this.downstreamNodeList = list.filter(item => !upstreamNodes.includes(item.key))
      } else {
        this.upstreamNodeList = list.filter(item => !downstreamNodes.includes(item.key))
      }
    },
    checkAssociated(node) {
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.readonlyNoAssociated'));
      if ([NODETYPE.SPARKSQL, NODETYPE.HQL, NODETYPE.SPARKPY, NODETYPE.SCALA, NODETYPE.PYTHON, NODETYPE.NEBULA].indexOf(node.type) === -1) {
        return this.$Notice.warning({
          desc: this.$t('message.workflow.process.noAssociated'),
        });
      } else if (node.jobContent && node.jobContent.script) {
        this.$Modal.confirm({
          title: this.$t('message.workflow.process.repeateAssociated'),
          content: this.$t('message.workflow.process.repeateAssociatedHtml'),
          okText: this.$t('message.workflow.process.confirmAssociated'),
          cancelText: this.$t('message.workflow.cancel'),
          onOk: () => {
            this.openAssociateScriptModal(node);
          },
          onCancel: () => {
            // this.$Modal.remove();
          },
        });
      } else {
        this.openAssociateScriptModal(node);
      }
    },
    openAssociateScriptModal(node) {
      this.$refs.associateScript.open(node);
    },
    associateScript(node, path, cb) {
      api.fetch('/filesystem/openFile', {
        path,
      }, 'get').then((rst) => {
        const supportModes = this.getSupportModes();
        const time = new Date();
        // 由于修改了节点类型所以之前获取方法不行
        const type = ext[node.type];
        const match = supportModes.find((item) => item.flowType === type);
        const fileName = `${time.getTime()}${match.ext}`;
        const params = {
          fileName,
          scriptContent: rst.fileContent[0][0],
          metadata: rst.metadata,
          projectName: this.$route.query.projectName || ''
        };
        if (node.resources && node.resources[0]) {
          params.resourceId = node.resources[0].resourceId;
        }
        if (params.metadata && params.metadata.configuration) delete params.metadata.configuration.startup;
        api.fetch('/filesystem/saveScriptToBML', params, 'post')
          .then((res) => {
            this.$Message.success(this.$t('message.workflow.process.associaSuccess'));
            node.params = {...node.params, ...rst.params};
            const params = {
              fileName,
              resourceId: res.resourceId,
              version: res.version,
              projectName: this.$route.query.projectName || ''
            };
            this.$emit('check-opened', node, (isOpened) => {
              this.$emit('save-node', params, node, true);
              if (isOpened) {
                api.fetch('/filesystem/openScriptFromBML', params, 'get').then((res) => {
                  this.dispatch('Workbench:updateFlowsTab', node, {
                    content: res.scriptContent,
                    params: res.metadata,
                  });
                });
              } else {
                // 如果没打开节点，是无法调取Workbench的方法的
                // 所以，直接调用IndexedDB清空缓存
                this.dispatch('IndexedDB:clearLog', node.key);
                this.dispatch('IndexedDB:clearResult', node.key);
                this.dispatch('IndexedDB:clearProgress', node.key);
              }
            });
            cb(true);
          }).catch(() => {
            cb(false);
            this.$Message.error(this.$t('message.workflow.process.associaError'));
          });
      }).catch(() => {
        cb(false);
      });
    },
    async addNode(node) {
      // 关闭右侧弹窗
      this.nodebaseinfoShow = false;
      // 新拖入的节点，自动生成新的不重复名称,给名称后面加四位随机数
      node = this.bindNodeBasicInfo(node);
      const templateList = await this.getTemplateDataByProject(node.type);
      this.clickCurrentNode = JSON.parse(JSON.stringify(node));
      this.clickCurrentNode.title = this.clickCurrentNode.title + '_' + Math.round(Math.random()*10000);
      // 弹窗提示由后台控制
      if (node.shouldCreationBeforeNode) {
        this.addNodeShow = true;
        this.addNodeTitle = this.$t('message.workflow.process.createNode');
      } else {
        // 还得同步更新json中的node
        this.json.nodes = this.json.nodes.map((subItem) => {
          if (subItem.key === this.clickCurrentNode.key) {
            subItem.title = this.clickCurrentNode.title;
            subItem.modifyUser = this.getUserName();
            subItem.modifyTime = Date.now();
            // 对于新增节点根据默认值情况做赋值
            if (this.tabs[0].data.isDefaultReference === '1') {
              templateList.forEach((v) => {
                if(v.workflowDefault) {
                  subItem.ecConfTemplateId = v.templateId;
                  subItem.ecConfTemplateName = v.templateName;
                  subItem.params = {
                    configuration: {
                      special: {},
                      runtime: {},
                      startup: {
                        'ec.conf.templateId': v.templateId,
                      }
                    }
                  }
                }
              });
            }
          }
          return subItem;
        });
        this.originalData = { ...this.json };
        this.autoSave(this.$t('message.workflow.AddNode'), false);
        return;
      }
    },
    validatorName(rule, value, callback) {
      if (value === `${this.name}_`) {
        callback(new Error(this.$t('message.workflow.process.nodeNameValid')));
      } else {
        callback();
      }
    },
    addFlowCancel() {
      this.addNodeShow = false;
      // 删除未创建成功的节点
      this.json.nodes = this.json.nodes.filter((subItem) => {
        return this.clickCurrentNode.key != subItem.key;
      });
      this.originalData = this.json;
    },
    // 创建节点时
    addFlowOk() {
      this.$refs.addFlowfoForm.validate((valid) => {
        if (valid) {
          this.addFlowOkFunction()
        }
      });
    },
    // addFlowOk函数里可以复用的操作
    addFlowOkFunction() {
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.readonlyNoCeated'));
      this.saveNodeBaseInfo(this.clickCurrentNode);
    },
    relySelect(node, dir) {
      /**
       * 1.获取当前节点的key
       * 2.查找以当前key为source的节点
       * 3.遍历查找出来的节点数组，接着递归
       *  */
      let stepArray = [];
      const stepArrayAction = (nodeKey, level = 0) => {
        level++;
        this.json.edges.forEach((item) => {
          if ( dir === 'down' && item.source === nodeKey) {
            stepArray.push({...item, level});
            stepArrayAction(item.target, level);
          } else if(dir === 'up' && item.target === nodeKey) {
            stepArray.push({...item, level});
            stepArrayAction(item.source, level);
          } else if(dir === 'down-one' && item.source === nodeKey) {
            stepArray.push({...item, level});
          } else if(dir === 'up-one' && item.target === nodeKey) {
            stepArray.push({...item, level});
          }
        });
      };
      stepArrayAction(node.key);
      this.json.nodes = this.json.nodes.map((subItem) => {
        let runState
        const inDeps = stepArray.filter((item) => (item.target === subItem.key && dir.includes('down')) || (item.source === subItem.key && dir.includes('up')))
        if (subItem.key === node.key) subItem.selected = true;
        if (inDeps.length ) {
          subItem.selected = true;
          // runState = {
          //   outerText: Math.max(...inDeps.map(it => it.level)),
          //   outerStyle: {
          //     position: 'absolute',
          //     top: '12px',
          //     right: '-30px',
          //     width: '30px',
          //     textAlign: 'center',
          //     color: 'rgb(237, 64, 20)',
          //   }
          // }
        } else {
          subItem.selected = false;
        }
        return {...subItem, runState};
      });
      this.originalData = { ...this.json };
    },
    heartBeat: throttle(() => {
      api.fetch('/user/heartbeat', 'get');
    }, 60000),
    copyNode(node) {
      node = this.bindNodeBasicInfo(node);
      if (node.enableCopy === false) return this.$Message.warning(this.$t('message.workflow.process.noCopy'));
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.readonlyNoCopy'));
      this.cacheNode = JSON.parse(JSON.stringify(node));
    },
    pasteNode(e) {
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.noPaste'));
      if (!this.cacheNode) {
        return this.$Message.warning(this.$t('message.workflow.process.firstCopy'));
      }
      let tmpTitle = this.cacheNode.title+'_copy'
      const hasNodeTitle = this.json.nodes.filter(it => it.title.indexOf(tmpTitle) > -1)
      if (hasNodeTitle.length) {
        tmpTitle = tmpTitle + hasNodeTitle.length
      }
      if (tmpTitle.length > 150) {
        return this.$Message.warning(this.$t('message.workflow.process.namelength'));
      }
      // 获取屏幕的缩放值
      let pageSize = 1
      if (this.$refs.process.getState) {
        pageSize = this.$refs.process.getState().baseOptions.pageSize;
      }
      const key = '' + new Date().getTime() + Math.ceil(Math.random() * 100);
      this.cacheNode.key = key;
      this.cacheNode.id = key;
      this.cacheNode.selected = true;
      this.cacheNode.title = tmpTitle
      this.cacheNode.createTime = Date.now()
      this.cacheNode.layout = {
        height: this.cacheNode.height,
        width: this.cacheNode.width,
        x: (e.offsetX / pageSize),
        y: (e.offsetY / pageSize),
      };
      // 外部节点以限制复制，如果要复制要删除id
      if (this.cacheNode.shouldCreationBeforeNode) {
        delete this.cacheNode.jobContent;
      }
      // 删掉节点的执行信心
      if(this.cacheNode.runState) {
        delete this.cacheNode.runState;
      }
      delete this.cacheNode.enableCopy;
      this.json.nodes = this.json.nodes.map((subItem) => {
        subItem.selected = false;
        return subItem;
      });
      this.json.nodes.push(JSON.parse(JSON.stringify(this.cacheNode)));
      this.originalData = { ...this.json };
      this.autoSave(this.$t('message.workflow.Saving'), false);
      this.click(this.cacheNode)
    },
    // 由于插件的selected不是响应式，所以得手动改变
    nodeSelectedFalse(node = {}) {
      this.json.nodes = this.json.nodes.map((subItem) => {
        if (node.key && node.key === subItem.key) {
          subItem.selected = true;
        } else {
          subItem.selected = false;
        }
        return subItem;
      });
      this.originalData = { ...this.json };
    },
    clickBaseInfo() {
      this.nodeSelectedFalse(this.clickCurrentNode);
    },    // 批量删除选中节点
    async allDelete() {
      if (this.myReadonly) return this.$Message.warning(this.$t('message.workflow.process.noDelete'));
      let selectNodes = this.$refs.process.getSelectedNodes();
      const selectNodeLength = selectNodes.length
      if (selectNodeLength < 1) return
      // 批量删除跳过子工作流节点
      selectNodes = selectNodes.filter(node => node.type !== NODETYPE.FLOW)
      const selectNodeKeys = selectNodes.map((item) => item.key);
      selectNodes = selectNodes.map((item) => this.bindNodeBasicInfo(item));
      const nodes = [];
      selectNodes.forEach(node=>{
        if (node.supportJump && node.shouldCreationBeforeNode && node.jobContent) {
          nodes.push({
            nodeType: node.type,
            name: node.title,
            description: node.desc,
            projectID: +this.$route.query.projectID,
            params: node.jobContent,
            labels: {
              route: this.getCurrentDsslabels()
            }
          })
        }
      })
      if (nodes.length) {
        await api.fetch(`${this.$API_PATH.WORKFLOW_PATH}batchDeleteAppConnNode`, {nodes}, 'post')
      }
      let msg = this.$t('message.workflow.deleteSuccess')
      if (selectNodeLength > selectNodes.length) {
        msg = this.$t('message.workflow.BatchDel');
        this.$Message.warning(msg);
      } else {
        this.$Message.success(msg);
      }
      selectNodes.forEach(node=>{
        this.$emit('deleteNode', node);
      })
      this.json.nodes = this.json.nodes.filter((item) => {
        if (selectNodeKeys.includes(item.key)) {
          this.json.edges = this.json.edges.filter((link) => {
            return !(link.source === item.key || link.target === item.key);
          });
        } else {
          item.selected = false;
          return true;
        }
      });
      this.originalData = { ...this.json };
      this.autoSave('allDelete', false);
    },
    /**
     * 右键菜单点击打开管理台
     */
    async openConsole(node) {
      if (this.$refs.bottomTab) this.$refs.bottomTab.closePanel()
      this.openningNode = null;
      this.nodeSelectedFalse(node); // 改变节点的选择状态
      // 将数据结构适配全局console组件
      node.runType = 'node'; // 新增运行类型字段
      node.taskID = node.runState.taskID; // 新增任务id
      node.execID = node.runState.execID; // 新增执行id
      this.consoleHeight = this.$el ? this.$el.clientHeight / 2 : 250
      this.shapeWidth = this.$refs.process && this.$refs.process.state.shapeOptions.viewWidth; // 自适应控制台宽度
      this.$nextTick(() => {
        this.openningNode = node; // 传给控制台的参数
        setTimeout(() => {
          this.$refs.currentConsole.checkFromCache();
        }, 50)
      })
    },
    closeConsole() {
      this.openningNode = null;
    },
    toggleShape(shapeFold) {
      // 工作流icon是否收起
      if (shapeFold) {
        this.shapeWidth = 0;
      } else {
        this.shapeWidth = this.$refs.process && this.$refs.process.state.shapeOptions.viewWidth;
      }
    },
    saveCommonIframe(node) {
      // 创建
      if (node.supportJump && node.shouldCreationBeforeNode && !node.jobContent) {
        const newCreateParams = this.getCreatePrams(node);
        const createParams = {
          flowID: this.flowId,
          projectID: +this.$route.query.projectID,
          nodeType: node.type,
          nodeID: node.key,
          params: {
            ...node.jobContent,
            ...newCreateParams
          },
          labels: {
            route: this.getCurrentDsslabels()
          }
        }
        this.loading = true;
        return api.fetch(`${this.$API_PATH.WORKFLOW_PATH}createAppConnNode`, createParams).then((res) => {
          // 由于vsbi的错误信息返回的这里，所以得判断是否成功给予提示
          let commomData = {};
          try {
            commomData = JSON.parse(res.result);
          } catch {
            commomData = res.result;
          }
          if (commomData) {
            node.jobContent= commomData;
            this.$Message.success({
              content: this.$t('message.workflow.process.createdSuccess')
            });
          } else {
            this.$Message.error({
              content: commomData.msg,
              duration: 4,
            });
          }
          // 创建成功关闭右侧栏
          this.nodebaseinfoShow = false;
        }).catch(() => {
          this.json.nodes = this.json.nodes.filter((subItem) => {
            return node.key != subItem.key;
          });
          this.originalData = { ...this.json };
        })
      }
      // 更新
      if (node.jumpType == 1 && node.jobContent) {
        const params = {
          flowID: this.flowId,
          nodeType: node.type,
          projectID: +this.$route.query.projectID,
          params: {
            ...node.jobContent,
            title: node.title,
            desc: node.desc
          },
          labels: {
            route: this.getCurrentDsslabels()
          }
        }
        this.loading = true;
        return api.fetch(`${this.$API_PATH.WORKFLOW_PATH}updateAppConnNode`, params, 'post').then(() => {
          this.$Message.success(this.$t('message.workflow.updataSuccess'))
        }).catch(() => {})
      }
    },
    // 获取需要在创建的时候填写的参数
    getCreatePrams(node) {
      const createParams = {}
      node.nodeUiVOS.filter((item) => item.baseInfo)
        .map(item => item.key).map(item => {
          createParams[item] = node[item];
        })
      return createParams;
    },
    // 根据节点类型将后台节点基础信息加入
    bindNodeBasicInfo(node) {
      if (node.nodeUiVOS) delete node.nodeUiVOS
      this.shapes.forEach((item) => {
        if (item.children.length > 0) {
          item.children.forEach((subItem) => {
            if (subItem.type === node.type || subItem.type === node.jobType) {
              node = Object.assign({}, subItem, node);
            }
          })
        }
      })
      return node;
    },
    // 点击节流
    clickswitch(type){
      if ( type === 'select') {
        let selectNodes = this.$refs.process.getSelectedNodes();
        const selectNodeLength = selectNodes.length
        if (selectNodeLength < 1 ) {
          return this.$Message.error(this.$t('message.workflow.PleaseSelectNode'));
        }
        selectNodes.forEach((node) => {
          this.$refs.process.setNodeRunState(node.key, {
          })
        })
      } else if(!this.workflowIsExecutor) {
        let json = JSON.parse(JSON.stringify(this.json));
        json.nodes.forEach((node) => {
          this.$refs.process.setNodeRunState(node.key, {
          })
        })
      }
      debounce(() => {
        if (this.workflowIsExecutor) {
          this.workflowStop()
        } else {
          this.workflowRun(type)
        }
      }, 1000)()
    },
    // 失败重跑
    reRun() {
      this.workflowRun('rerun')
    },
    async workflowRun(runFlag) {
      if (this.$refs.bottomTab) this.$refs.bottomTab.closePanel()
      this.retryTimes = 0
      let selectNodes = this.$refs.process.getSelectedNodes();
      this.dispatch('workflowIndexedDB:clearNodeCache');
      // 重新执行清掉上次的计时器
      clearTimeout(this.excuteTimer);
      clearTimeout(this.executorStatusTimer);
      this.needReRun = false;
      this.openningNode = null;
      // return this.$Message.warning('执行重构中，即将开源');
      /**
       * 1.执行之前先保存，执行改为停止
       * 2.禁用操作：左侧菜单，保存，参数修改，工具栏，更具状态来操作右键
       * 3.轮询接口获取节点状态
      */
      // 如果是生产中心的只读模式不需要保存
      let a = null;
      if (!this.myReadonly) {
        a = await this.autoSave(this.$t('message.workflow.Saving'), false);
        if (!a || !a.flowVersion) return;

      }
      // 保存成功后再调执行接口
      const parmas = {
        executeApplicationName: "flowexecution",
        executionCode: JSON.stringify({
          flowId: this.flowId,
          version: !this.myReadonly ? a.flowVersion : this.flowVersion
        }),
        runType: "json",
        params: {},
        labels: {
          route: this.getCurrentDsslabels()
        },
        source: {
          projectName: this.$route.query.projectName,
          flowName: this.name
        },
        requestApplicationName: "flowexecution"
      }
      if (runFlag === 'rerun') {
        parmas.isReExecute = true
      }
      const exeUrl = '/dss/flow/entrance/execute'
      if ( runFlag === 'select') {
        parmas.nodeID = selectNodes.map(item=>item.key).join(',')
        parmas.isSelectedExecute = true
      }
      api.fetch(exeUrl, parmas).then((res) => {
        // 每次执行之后缓存工作流，关闭重新打开再接着获取状态

        this.workflowExecutorCache.push({
          flowId: this.flowId,
          execID: res.execID,
          taskID: res.taskID
        })
        // 查询执行节点的状态
        let execID = res.execID;
        let taskID = res.taskID;
        this.workflowTaskId = res.taskID;
        this.workflowExeteId = execID;
        this.queryWorkflowExecutor(execID, taskID)
      }).catch(() => {
        this.workflowIsExecutor = false;
      })
      this.workflowIsExecutor = true;
    },
    workflowStop() {
      this.retryTimes = 0
      clearTimeout(this.excuteTimer);
      clearTimeout(this.executorStatusTimer);
      // 清掉当前工作流执行的缓存
      this.workflowExecutorCache = this.workflowExecutorCache.filter((item) => {
        item.flowId !== this.flowId;
      });
      if (this.task_killing) {
        return this.$Message.error('请求已发出，请勿重复点击');
      }
      this.task_killing = true
      api.fetch(`/dss/flow/entrance/${this.workflowExeteId}/kill`, {taskID: this.workflowTaskId, labels: this.getCurrentDsslabels()}, 'get').then(() => {
        this.workflowIsExecutor = false;
        this.flowExecutorNode(this.workflowExeteId, true);
        this.task_killing = false;
      }).catch(() => {
        this.workflowIsExecutor = false;
        this.task_killing = false;
      })
    },
    queryWorkflowExecutor(execID, taskID) {
      this.retryTimes = this.retryTimes || 0;
      api.fetch(`/dss/flow/entrance/${execID}/status`,
        {
          taskID,
          labels: this.getCurrentDsslabels()
        }, 'get').then((res) => {
        this.flowExecutorNode(execID);
        // 根据执行状态判断是否轮询
        const status = res.status;
        if (status === 3) { // 停止状态轮询
          if (res.message) {
            this.$Message.error(res.message);
          }
          this.workflowIsExecutor = false;
          return
        }
        if (['Inited', 'Scheduled', 'Running'].includes(status)) {
          if (this.excuteTimer) {
            clearTimeout(this.excuteTimer);
            this.excuteTimer = null;
          }
          this.excuteTimer = setTimeout(() => {
            this.queryWorkflowExecutor(execID, taskID);
          }, 1000)
        } else {
          // Succees, Failed, Cancelled, Timeout
          this.workflowIsExecutor = false;
          // 工作流执行状态和节点执行状态轮询不同步，工作流执行成功后，若节点执行状态尚未成功，再次查询更新进度 dpms 312293
          if (this.openningNode) {
            setTimeout(()=> {
              this.$refs.currentConsole.queryState(false);
            }, 1000)
          }
          if (status === 'Succeed') {
            this.$Notice.success({desc: this.$t('message.common.projectDetail.workflowRunSuccess')})
          }
          if (status === 'Failed') {
            this.$Notice.error({desc: this.$t('message.common.projectDetail.workflowRunFail')})
            this.flowExecutorNode(execID, true);
            this.$refs.bottomTab.showPanel('execHistory', res.logPath);
          }
          if (status === 'Cancelled') {
            this.$Notice.error({desc: this.$t('message.common.projectDetail.workflowRunCanceled')})
            this.flowExecutorNode(execID, true);
          }
          if (status === 'Timeout') {
            this.$Notice.error({desc: this.$t('message.common.projectDetail.workflowRunOvertime')})
            this.flowExecutorNode(execID, true);
          }
          // 清掉当前工作流执行的缓存
          this.workflowExecutorCache = this.workflowExecutorCache.filter((item) => {
            return item.flowId !== this.flowId;
          });
        }
      }).catch(() => {
        // 失败重试5次
        if (this.retryTimes < 5) {
          clearTimeout(this.excuteTimer);
          this.excuteTimer = null;
          this.excuteTimer = setTimeout(() => {
            this.retryTimes = this.retryTimes + 1;
            this.queryWorkflowExecutor(execID, taskID);
          }, 1000)
        } else {
          this.retryTimes = 0
          this.flowExecutorNode(execID, true);
        }
        this.$Notice.error({desc: this.$t('message.common.projectDetail.workflowRunFail')})
      })
    },
    flowExecutorNode(execID, end = false) {
      api.fetch(`/dss/flow/entrance/${execID}/execution`, {labels: this.getCurrentDsslabels()}, 'get').then((res) => {
        // 【0：未执行；1：运行中；2：已成功；3：已失败；4：已跳过】
        const actionStatus = {
          pendingJobs: {color: '#6A85A7', status: 0, iconType: '',
            colorClass: '', isShowTime: false, title: this.$t('message.workflow.Scheduled'), showConsole: false},
          runningJobs: {color: '#2E92F7', status: 1, iconType: 'status-loading',
            colorClass: {'executor-loading': true}, isShowTime: true, title: this.$t('message.workflow.Running'), showConsole: true},
          succeedJobs: {color: '#52C41A', status: 2,iconType: 'status-success',
            colorClass: {'executor-success': true}, isShowTime: false, title: this.$t('message.workflow.ExecuteSuccess'), showConsole: true},
          failedJobs: {color: '#FF4D4F', status: 3, iconType: 'status-fail',
            colorClass: {'executor-faile': true}, isShowTime: false, title: this.$t('message.workflow.ExecuteFailed'), showConsole: true},
          skippedJobs: {color: '#B3C1D3', status: 4, iconType: 'status-skip',
            colorClass: {'executor-skip': true}, isShowTime: false, title: this.$t('message.workflow.Skip'), showConsole: false}
        };
        // 获取节点的状态，如果没有执行完成继续查询
        const  data = res;
        Object.keys(data).forEach((key) => {
          // 如果当前工作流已经执行结束，还得获取状态到没有执行的节点为止
          if(end && key === 'runningJobs' && data[key].length > 0) {
            // 手动停掉执行和切换页面停止调接口
            this.executorStatusTimer = setTimeout(() => {
              this.flowExecutorNode(execID, true);
            }, 2000)
          }
          data[key].forEach((node) => {
            if (this.$refs.process) {
              let time = 0
              if (node.startTime) {
                time = node.nowTime - node.startTime
              }
              this.$refs.process.setNodeRunState(node.nodeID, {
                time: this.timeTransition(time),
                status: actionStatus[key].status,
                borderColor: actionStatus[key].color,
                iconType: actionStatus[key].iconType,
                colorClass: actionStatus[key].colorClass,
                isShowTime: actionStatus[key].isShowTime,
                title: actionStatus[key].title,
                showConsole: actionStatus[key].showConsole,
                execID: node.execID,
                taskID: node.taskID,
                isSvg: true
              }, this.myReadonly && !this.product)
            }
          })
        })
        if (end && data.failedJobs && data.failedJobs.length) {
          this.needReRun = true
        }
      })
    },
    timeTransition(time) {
      time =Math.floor(time / 1000)
      let hour = 0;
      let minute = 0;
      let second = 0;
      // let str ="00:00:00";
      // if (time < 0) return str;
      if (time > 60) {
        minute = Math.floor(time / 60);
        second = Math.floor(time % 60);
        if (minute >= 60) {
          hour = Math.floor(minute / 60);
          minute = Math.floor(minute % 60);
        } else {
          hour = 0;
        }
      } else {
        hour = 0;
        if (time == 60) {
          minute = 1;
          second = 0;
        } else {
          minute = 0;
          second = time;
        }
      }
      const addZero = (num) => {
        let result = num;
        if (num < 10) {
          result = `0${num}`
        }
        return result;
      }
      const timeResult = `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`;
      time=0;
      return timeResult;
    },
    workflowPublishIsShow() {
      // 已经在发布不能再点击
      if(this.isFlowPubulish) return this.$Message.warning(this.$t('message.workflow.publishing'))
      this.pubulishShow = true;
      this.saveingComment = false;
      this.pubulishFlowComment = ''
      this.publishFlowData = [];
      // 未关联Git的不用查询
      if (this.associateGit) {
        api.fetch('/dss/framework/orchestrator/publish/history',
          {
            projectName: this.$route.query.projectName,
            orchestratorId: this.orchestratorId,
            workspaceId: this.$route.query.workspaceId
          }, 
          'get').then((rst) => {
              this.publishFlowData = rst.history.responses || [];
          });
        }
    },
    showDiff() {
      this.pubulishShow = false;
      this.$refs.bottomTab.showPanel('version');
    },
    async handleWorkflowPublish() {
      const params = {
        orchestratorId: this.orchestratorId,
        projectId: this.$route.query.projectID,
      }
      const rst = await api.fetch('/dss/framework/orchestrator/publishFlowCheck', params, 'get');
      if(rst && rst.data && rst.data.notContainsKeywordsNodeList && rst.data.notContainsKeywordsNodeList.length>0){
        const content = `<p class="ellipse-p">工作流${rst.data.orchestratorName}中节点：${rst.data.notContainsKeywordsNodeList.join(',')}</p><p>不包含关键字insert或create table</p>`;
        this.$Modal.confirm({
            title: '节点关键字检查',
            content: content,
            okText: '继续发布',
            cancelText:'返回修改',
            onOk: () => {
              this.workflowPublish();
            },
        });
      }else {
        this.workflowPublish()
      }
    },
    async workflowPublish() {
      // 只有未接入Git的项目发布前需求保存
      if (!this.associateGit) {
        if (this.saveingComment) {
            return
        }
        this.saveingComment = true
        // 发布之前先保存
        let a
        try {
          a = await this.autoSave(this.$t('message.workflow.Publishwork'), false);
        } catch (e) {
          this.pubulishShow = false;
          this.isFlowPubulish = false;
        }
        if (!a) {
          this.pubulishShow = false;
          this.isFlowPubulish = false;
          this.saveingComment = false;
          return;
        }  
      }
      if (this.flowProxyUser) {
        let isPassed = true;
        try {
          const rst = await api.fetch('/dss/framework/workspace/isDismissed', {usernames: [this.flowProxyUser]}, 'post');
          if (rst && (rst.isDismissed || []).some(item => Object.values(item)[0])) {
            this.$Message.warning(`${this.name}工作流的代理用户已离职或不存在，请确认是否修改代理用户`);
            isPassed = false;
          }
        } catch (e) {
          isPassed = false;
        }
        if (!isPassed) {
          this.saveingComment = false;
          return;
        }
      }
      // 调用发布接口
      const params = {
        orchestratorId: this.orchestratorId,
        orchestratorVersionId: this.orchestratorVersionId,
        dssLabel: this.getCurrentDsslabels(),
        workflowId: Number(this.flowId),
        labels: {route: this.getCurrentDsslabels()},
        comment: this.pubulishFlowComment
      }
      // 记录工作流是否在发布
      this.isFlowPubulish = true;
      api.fetch(`/dss/workflow/publishWorkflow`, params, 'post').then((res) => {
        this.pubulishShow = false;
        // 发布之后需要轮询结果
        let queryTime = 0;
        this.saveingComment = false;
        this.checkResult(res.releaseTaskId, queryTime, 'publish');
        this.setTaskId(res.releaseTaskId);
      }).catch(() => {
        this.pubulishShow = false;
        this.isFlowPubulish = false;
        this.saveingComment = false;
        this.$Message.error(this.$t('message.common.projectDetail.publishFailed'));
      })
    },
    // 发布和导出共用查询接口
    checkResult(id, timeoutValue, type = 'publish') {
      let typeName = this.$t('message.workflow.export')
      if (type === 'publish') {
        typeName = this.$t('message.workflow.process.publish')
      }
      this.timer = setTimeout(() => {
        timeoutValue += 2000;
        getPublishStatus(+id, this.getCurrentDsslabels()).then((res) => {
          if (timeoutValue <= (10 * 60 * 1000)) {
            if (res.status === 'init' || res.status === 'running') {
              clearTimeout(this.timer);
              this.checkResult(id, timeoutValue, type);
            } else if (res.status === 'success') {
              clearTimeout(this.timer);
              this.isFlowPubulish = false;
              this.$emit('updateFlowStatus');
              // 如果是导出成功需要下载文件
              if (type === 'export' && res.msg) {
                const url = module.data.API_PATH + 'dss/downloadFile/' + res.msg;
                const link = document.createElement('a');
                link.setAttribute('href', url);
                link.setAttribute('download', '');
                const evObj = document.createEvent('MouseEvents');
                evObj.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, true, false, 0, null);
                const flag = link.dispatchEvent(evObj);
                this.$nextTick(() => {
                  if (flag) {
                    this.$Message.success(this.$t('message.workflow.downloadTolocal'));
                  }
                });
              }
              this.$Message.success(this.$t('message.workflow.workflowSuccess', { name: typeName }));
              // 发布成功后，根工作流id会变化，导致修改工作流后保存的还是旧id
              this.refreshOpen()
            } else if (res.status === 'failed') {
              clearTimeout(this.timer);
              this.isFlowPubulish = false;
              this.$Modal.error({
                title: this.$t('message.workflow.workflowFail', { name: typeName }),
                content: `<p style="word-break: break-all;">${res.errorMsg}</p>`,
                width: 500,
                okText: this.$root.$t('message.workflow.publish.cancel'),
              });
              // 发布成功后，根工作流id会变化，导致修改工作流后保存的还是旧id
              this.refreshOpen()
            }
          } else {
            clearTimeout(this.timer);
            this.isFlowPubulish = false;
            this.$Message.warning(this.$t('message.common.projectDetail.workflowRunOvertime'));
          }
          // 扩展插件发布历史列表更新
          eventbus.emit('get_publish_status', res)
        }).catch(()=> {
          this.isFlowPubulish = false;
          this.refreshOpen()
        });
      }, 2000);
    },
    // 提交
    submitGit() {
      if (this.isFlowSubmit) return
      const params = {
        orchestratorId: this.orchestratorId,
        flowId: Number(this.flowId),
        labels: {route: this.getCurrentDsslabels()},
        projectName: this.$route.query.projectName,	
        comment: this.submitDesc,
      };
      this.showSubmit = false;
      this.isFlowSubmit = true;
      this.setTaskId(this.orchestratorId, 'submit');
      api.fetch('/dss/framework/orchestrator/submitFlow', params, 'post').then(res => {
        this.submitDesc = '';
        this.checkSubmitStatus('submit');
      }).catch(() => {
        this.isFlowSubmit = false;
        this.removeTaskId('submit');
      });
    },
    // 检查提交状态
    checkSubmitStatus(flag) {
      const typeName = this.$t('message.workflow.process.submitgit');
      const publishTaskId = this.getTaskId('submit')
      if (publishTaskId && this.orchestratorId == publishTaskId) {
        api.fetch('/dss/framework/orchestrator/submitFlow/status', {orchestratorId: this.orchestratorId,}, 'get').then(res => {
          if (res.status == 'running') {
            this.isFlowSubmit = true;
            setTimeout(() => {
              this.checkSubmitStatus(flag);
            }, 2000)
          } else if (res.status == 'success') {
            this.removeTaskId('submit');
            this.isFlowSubmit = false;
            this.$emit('updateWorkflowList');
            this.$emit('updateFlowStatus');
            if (flag === 'submit') {
              this.$Message.success(this.$t('message.workflow.workflowSuccess', { name: typeName }));
            }
          } else if(res.status == 'failed') {
            this.removeTaskId('submit');
            this.isFlowSubmit = false;
            if (flag === 'submit') {
              this.$Message.warning(this.$t('message.workflow.workflowFail', { name: typeName }));
            }
          }
        }).catch(() => {
          this.isFlowSubmit = false;
        });
      }
    },
    refreshOpen() {
      this.$emit('close')
      this.$emit('open')
    },
    dateFormatter(date) {
      return moment(date).format('YYYY-MM-DD HH:mm:ss');
    },
    exportWorkflow() {
      /*
      1.导出时，添加描述，和选择是否同步发版
      2.在导出之前得先保存工作流
      3.先调用导出接口，成功后再下载到本地
      */
      if(this.isFlowPubulish) return this.$Message.warning(this.$t('message.workflow.warning.api'))
      this.workflowExportShow = true;

    },
    async workflowExportOk() {
      const a = await this.autoSave(this.$t('message.workflow.Export'), false);
      if (!a) return
      this.isFlowPubulish = true;
      const params = {
        rootFlowID: Number(this.flowId),
        // projectVersionID: +this.projectVersionID,
        IOType: 'FLOW',
        comment: this.exportDesc,
        needChangeVersion: this.exportChangeVersion
      }
      api.fetch("export", params, 'post').then(() => {
        // let queryTime = 0;
        // this.checkResult(+this.projectVersionID, queryTime, 'export');
      })
    },
    // 删除工作流的线触发自动保存
    linkDelete() {
      const timerId = setTimeout(() => {
        this.autoSave('deleteLink', true);
        clearTimeout(timerId);
      }, 500);
      if (this.viewMode === 'cyeditor') {
        this.originalData = this.json;
      }
    },
    linkAdd() {
      const timerId = setTimeout(() => {
        this.autoSave('addLink', true);
        clearTimeout(timerId);
      }, 500);
      if (this.viewMode === 'cyeditor') {
        this.originalData = this.json;
      }
    },
    closeParamsBar() {
      // 关闭参数参数窗口
      this.nodebaseinfoShow = false;
    },
    // 获取控制台设置的参数信息
    getConsoleParams() {
      Promise.all([api.fetch('/configuration/getFullTreesByAppName', {
        engineType: 'spark',
        creator: 'nodeexecution',
      }, 'get'),
      api.fetch('/configuration/getFullTreesByAppName', {
        engineType: '通用设置',
        creator: '通用设置',
      }, 'get'),
      api.fetch('/configuration/getFullTreesByAppName', {
        engineType: 'hive',
        creator: 'nodeexecution',
      }, 'get')]).then((res) => {
        this.consoleParams = res;
      }).catch(() => {

      })
    },
    // 获取工作空间名称
    getCurrentWorkspaceName() {
      const workspaceData = storage.get("currentWorkspace");
      return workspaceData ? workspaceData.name : ''
    },
    getTaskKey(type = 'taskId'){
      const username = this.getUserName();
      const key = `${username}-workflow-${this.orchestratorId}-${type}`;
      return key
    },
    setTaskId(taskId, type) {
      const key = this.getTaskKey(type);
      storage.set(key, taskId);
    },
    getTaskId(type){
      const key = this.getTaskKey(type);
      return storage.get(key);
    },
    removeTaskId(type) {
      const key = this.getTaskKey(type);
      storage.remove(key);
    },
    onKeyUp(e) {
      const isDel = e.keyCode === 46 || e.keyCode === 8 && navigator.userAgent.indexOf('Mac') !== -1
      if (e.altKey && e.ctrlKey) { return }
      if (isDel && this.tabs[this.$parent.active].key === this.activeTabKey && e.target.nodeName!='INPUT' && e.target.nodeName!='TEXTAREA') {
        if (this.myReadonly) return
        let selectNodes = this.$refs.process.getSelectedNodes();
        const selectNodeLength = selectNodes.length
        if (selectNodeLength > 0) {
          if (selectNodeLength > 1) {
            this.allDelete();
          } else {
            this.nodeDelete(selectNodes[0])
          }
        }
      }
    },
    async checkLastPublish(cb) {
      const publishTaskId = this.getTaskId()
      if (publishTaskId && this.orchestratorId == this.$route.query.flowId) {
        let res
        try {
          res = await getPublishStatus(publishTaskId, this.getCurrentDsslabels())
        } catch (error) {
          this.removeTaskId()
        }
        if (res && res.status === 'running') {
          this.isFlowPubulish = true
          this.checkResult(publishTaskId, 0, 'publish')
          // 打开发布历史panel
          if (cb) {
            cb()
          }
        }
      }
    },
    showSearchPath() {
      this.showNodePathPanel = true
    },
    changeViewMode(mode, isSave = false) {
      if (this.viewMode === mode) return
      if (this.jsonChange) {
        return this.message({
          type: 'error',
          msg: '请先保存'
        })
      }
      if (mode == 'table') {
        this.originalData = this.json;
        this.iframeloading = true
        this.openningNode = null
        if (this.viewMode !== 'table') {
          this.preDragViewMode = this.viewMode
        }
        this.viewMode = mode
        this.$nextTick(()=> {
          const ifr = this.$refs.ifr;
          if (ifr) {
            ifr.onload = () => {
              this.iframeloading = false
            }
          }
        })
      } else if(mode || this.preDragViewMode) {

        this.viewMode = mode || this.preDragViewMode
        if (this.viewMode !== 'table') {
          this.preDragViewMode = this.viewMode
        }
        // 切换至原拖拽模式，节点位置负值处理
        if (this.viewMode === 'vueprocess') {
          let x = 0
          let y = 0
          this.json.nodes.map(it => {
            if (it.layout.x < x) {
              x = it.layout.x
            }
            if (it.layout.y < y) {
              y = it.layout.y
            }
          })
          if (x < 0 || y < 0) {
            this.json.nodes.forEach(element => {
              element.layout.x = element.layout.x + x * -1
              element.layout.y = element.layout.y + y * -1
            });
          }
        }
        this.originalData = this.json;
        // 切换模式后保存数据，确保模式也被更新
		    if (isSave && !this.product) {
          this.autoSave(this.$t('message.workflow.Save'), false);
        }
      }
    },
    screenSizeChange(fullScreen) {
      this.isfullScreen = fullScreen
    },
    showSubmitGit() {
      if (this.jsonChange) {
        this.autoSave(this.$t('message.workflow.Save'), false);
      }
      this.showSubmit = true;
    },
    handleSwitchViewMode(action, arg) {
      this[action](arg, true)
    },
    handleClickToolbar(action, arg) {
      this[action](arg)
    }
  }
}
</script>
<style src="./index.scss" lang="scss">
.ellipse-p {
  word-break: break-word;
  overflow-wrap: break-word;
}
</style>
